Compare commits
15 Commits
faba2dee5d
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e55418703 | |||
| 56b442fdff | |||
| 35d7ab38fa | |||
| 070d682759 | |||
| cd56ab8fb6 | |||
| b012fcaaac | |||
| 7a1d3e7917 | |||
| 2ee3caac0d | |||
| 78e93e9766 | |||
| 83d66feacc | |||
| d6ace14e70 | |||
| 2cbfd21539 | |||
| f7c070e45b | |||
| 06ed9734c6 | |||
| d93d4c6cd0 |
@@ -21,7 +21,7 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Analyze ${{ matrix.language }}
|
name: Analyze ${{ matrix.language }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: pwap-runner
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ jobs:
|
|||||||
# ========================================
|
# ========================================
|
||||||
web:
|
web:
|
||||||
name: Web App
|
name: Web App
|
||||||
runs-on: ubuntu-latest
|
runs-on: pwap-runner
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
@@ -92,7 +92,7 @@ jobs:
|
|||||||
# ========================================
|
# ========================================
|
||||||
build-image:
|
build-image:
|
||||||
name: Build & Push Image
|
name: Build & Push Image
|
||||||
runs-on: ubuntu-latest
|
runs-on: pwap-runner
|
||||||
needs: [web, telegram-gate]
|
needs: [web, telegram-gate]
|
||||||
if: |
|
if: |
|
||||||
github.ref == 'refs/heads/main' &&
|
github.ref == 'refs/heads/main' &&
|
||||||
@@ -101,14 +101,21 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
id-token: write # cosign keyless signing via Sigstore OIDC
|
||||||
outputs:
|
outputs:
|
||||||
image_sha: ${{ steps.meta.outputs.image_sha }}
|
image_sha: ${{ steps.meta.outputs.image_sha }}
|
||||||
|
image_digest: ${{ steps.build.outputs.digest }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: docker/setup-buildx-action@v3
|
- uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Install cosign
|
||||||
|
uses: sigstore/cosign-installer@v3
|
||||||
|
with:
|
||||||
|
cosign-release: 'v2.4.1'
|
||||||
|
|
||||||
- name: Log in to GHCR
|
- name: Log in to GHCR
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
@@ -125,6 +132,7 @@ jobs:
|
|||||||
echo "image=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" >> $GITHUB_OUTPUT
|
echo "image=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
|
id: build
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: ./
|
context: ./
|
||||||
@@ -146,6 +154,18 @@ jobs:
|
|||||||
cache-to: type=registry,ref=${{ steps.meta.outputs.image }}:cache,mode=max
|
cache-to: type=registry,ref=${{ steps.meta.outputs.image }}:cache,mode=max
|
||||||
provenance: false
|
provenance: false
|
||||||
|
|
||||||
|
- name: Sign image with cosign (keyless, Sigstore Fulcio)
|
||||||
|
env:
|
||||||
|
COSIGN_EXPERIMENTAL: '1'
|
||||||
|
run: |
|
||||||
|
IMAGE_DIGEST="${{ steps.meta.outputs.image }}@${{ steps.build.outputs.digest }}"
|
||||||
|
# cosign needs its own registry auth — docker/login-action only writes
|
||||||
|
# ~/.docker/config.json which cosign on self-hosted runner can't read
|
||||||
|
echo "${{ secrets.GITHUB_TOKEN }}" | cosign login ghcr.io -u "${{ github.actor }}" --password-stdin
|
||||||
|
echo "Signing $IMAGE_DIGEST"
|
||||||
|
cosign sign --yes "$IMAGE_DIGEST"
|
||||||
|
echo "✅ Image signed (transparency log: rekor.sigstore.dev)"
|
||||||
|
|
||||||
# ========================================
|
# ========================================
|
||||||
# TELEGRAM CEO APPROVAL GATE
|
# TELEGRAM CEO APPROVAL GATE
|
||||||
# Runs on self-hosted pwap-runner (DEV VPS) where pexsec-bot.service
|
# Runs on self-hosted pwap-runner (DEV VPS) where pexsec-bot.service
|
||||||
@@ -215,7 +235,7 @@ jobs:
|
|||||||
# ========================================
|
# ========================================
|
||||||
bump-version:
|
bump-version:
|
||||||
name: Bump Version
|
name: Bump Version
|
||||||
runs-on: ubuntu-latest
|
runs-on: pwap-runner
|
||||||
needs: [web, security-audit, telegram-gate, build-image]
|
needs: [web, security-audit, telegram-gate, build-image]
|
||||||
# Skip on rollback (workflow_dispatch with rollback_to set)
|
# Skip on rollback (workflow_dispatch with rollback_to set)
|
||||||
if: |
|
if: |
|
||||||
@@ -261,7 +281,7 @@ jobs:
|
|||||||
# ========================================
|
# ========================================
|
||||||
deploy-app:
|
deploy-app:
|
||||||
name: Deploy app.pezkuwichain.io
|
name: Deploy app.pezkuwichain.io
|
||||||
runs-on: ubuntu-latest
|
runs-on: pwap-runner
|
||||||
needs: [telegram-gate, bump-version, build-image]
|
needs: [telegram-gate, bump-version, build-image]
|
||||||
if: |
|
if: |
|
||||||
always() &&
|
always() &&
|
||||||
@@ -286,6 +306,14 @@ jobs:
|
|||||||
echo "sha=${{ needs.build-image.outputs.image_sha }}" >> $GITHUB_OUTPUT
|
echo "sha=${{ needs.build-image.outputs.image_sha }}" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Capture currently-live SHA (for auto-rollback)
|
||||||
|
id: prev
|
||||||
|
run: |
|
||||||
|
# /.deploy-sha is written into every deploy; read what's live now
|
||||||
|
PREV=$(curl -sf --max-time 5 "https://${{ env.DOMAIN }}/.deploy-sha" | head -c 40 | tr -dc 'a-f0-9' || echo "")
|
||||||
|
echo "Previous live SHA: ${PREV:-unknown}"
|
||||||
|
echo "prev=$PREV" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Log in to GHCR
|
- name: Log in to GHCR
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
@@ -293,6 +321,24 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install cosign (for verify)
|
||||||
|
uses: sigstore/cosign-installer@v3
|
||||||
|
with:
|
||||||
|
cosign-release: 'v2.4.1'
|
||||||
|
|
||||||
|
- name: Verify image signature (cosign keyless)
|
||||||
|
env:
|
||||||
|
COSIGN_EXPERIMENTAL: '1'
|
||||||
|
run: |
|
||||||
|
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.sha.outputs.sha }}"
|
||||||
|
echo "${{ secrets.GITHUB_TOKEN }}" | cosign login ghcr.io -u "${{ github.actor }}" --password-stdin
|
||||||
|
echo "Verifying signature for $IMAGE"
|
||||||
|
cosign verify "$IMAGE" \
|
||||||
|
--certificate-identity-regexp "^https://github.com/pezkuwichain/pwap/.github/workflows/quality-gate.yml@" \
|
||||||
|
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
|
||||||
|
> /dev/null
|
||||||
|
echo "✅ Signature valid — image was built by trusted CI"
|
||||||
|
|
||||||
- name: Extract /dist from image
|
- name: Extract /dist from image
|
||||||
run: |
|
run: |
|
||||||
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.sha.outputs.sha }}"
|
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.sha.outputs.sha }}"
|
||||||
@@ -301,7 +347,8 @@ jobs:
|
|||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
docker cp "$CID:/dist/." dist/
|
docker cp "$CID:/dist/." dist/
|
||||||
docker rm "$CID" >/dev/null
|
docker rm "$CID" >/dev/null
|
||||||
echo "Extracted dist/ contents:"
|
# Stamp this build's SHA into dist so future deploys can read PREV
|
||||||
|
echo "${{ steps.sha.outputs.sha }}" > dist/.deploy-sha
|
||||||
ls -la dist/ | head -10
|
ls -la dist/ | head -10
|
||||||
|
|
||||||
- name: Deploy to DEV VPS
|
- name: Deploy to DEV VPS
|
||||||
@@ -329,6 +376,48 @@ jobs:
|
|||||||
echo "❌ Health check failed for ${{ env.DOMAIN }}"
|
echo "❌ Health check failed for ${{ env.DOMAIN }}"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
|
# ── Automatic rollback: pull PREV SHA image, redeploy, recheck ──
|
||||||
|
- name: Auto-rollback to previous SHA
|
||||||
|
id: rollback
|
||||||
|
if: failure() && steps.healthcheck.conclusion == 'failure' && steps.prev.outputs.prev != ''
|
||||||
|
run: |
|
||||||
|
PREV="${{ steps.prev.outputs.prev }}"
|
||||||
|
echo "🔄 Rolling back ${{ env.DOMAIN }} to $PREV"
|
||||||
|
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:$PREV"
|
||||||
|
docker pull "$IMAGE"
|
||||||
|
CID=$(docker create "$IMAGE")
|
||||||
|
rm -rf dist && mkdir dist
|
||||||
|
docker cp "$CID:/dist/." dist/
|
||||||
|
docker rm "$CID" >/dev/null
|
||||||
|
echo "$PREV" > dist/.deploy-sha
|
||||||
|
echo "rollback_sha=$PREV" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: SCP rollback artifact
|
||||||
|
if: steps.rollback.outcome == 'success'
|
||||||
|
uses: appleboy/scp-action@v1.0.0
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.VPS_HOST }}
|
||||||
|
username: ${{ secrets.VPS_USER }}
|
||||||
|
key: ${{ secrets.VPS_SSH_KEY }}
|
||||||
|
port: ${{ secrets.VPS_SSH_PORT || 2222 }}
|
||||||
|
source: 'dist/*'
|
||||||
|
target: '/var/www/subdomains/app'
|
||||||
|
strip_components: 1
|
||||||
|
|
||||||
|
- name: Re-health-check after rollback
|
||||||
|
if: steps.rollback.outcome == 'success'
|
||||||
|
id: healthcheck_rb
|
||||||
|
run: |
|
||||||
|
for i in 1 2 3 4 5 6; do
|
||||||
|
if curl -sf --max-time 10 "https://${{ env.DOMAIN }}/" >/dev/null; then
|
||||||
|
echo "✅ Rolled back successfully — ${{ env.DOMAIN }} healthy on ${{ steps.rollback.outputs.rollback_sha }}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
echo "❌ Rollback also failed!"
|
||||||
|
exit 1
|
||||||
|
|
||||||
- name: Post-deploy notification
|
- name: Post-deploy notification
|
||||||
if: success()
|
if: success()
|
||||||
run: |
|
run: |
|
||||||
@@ -339,17 +428,29 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
BOT_TOKEN: ${{ secrets.PEXSEC_BOT_TOKEN }}
|
BOT_TOKEN: ${{ secrets.PEXSEC_BOT_TOKEN }}
|
||||||
CEO_CHAT_ID: ${{ secrets.TELEGRAM_CEO_CHAT_ID }}
|
CEO_CHAT_ID: ${{ secrets.TELEGRAM_CEO_CHAT_ID }}
|
||||||
|
NEW_SHA: ${{ steps.sha.outputs.sha }}
|
||||||
|
PREV_SHA: ${{ steps.prev.outputs.prev }}
|
||||||
|
ROLLBACK_OUTCOME: ${{ steps.rollback.outcome }}
|
||||||
|
RECHECK_OUTCOME: ${{ steps.healthcheck_rb.outcome }}
|
||||||
run: |
|
run: |
|
||||||
|
if [ "$RECHECK_OUTCOME" = "success" ]; then
|
||||||
|
MSG="⚠️ pwap/web ${{ env.DOMAIN }}: deploy ($NEW_SHA) failed health check, AUTO-ROLLED-BACK to $PREV_SHA. Site healthy."
|
||||||
|
elif [ "$ROLLBACK_OUTCOME" = "success" ]; then
|
||||||
|
MSG="🚨 pwap/web ${{ env.DOMAIN }}: deploy ($NEW_SHA) failed AND rollback to $PREV_SHA also failed. Manual intervention needed."
|
||||||
|
elif [ -z "$PREV_SHA" ]; then
|
||||||
|
MSG="❌ pwap/web ${{ env.DOMAIN }}: deploy ($NEW_SHA) failed. No previous SHA available (first deploy?). Manual rollback: gh workflow run quality-gate.yml -f rollback_to=<sha>"
|
||||||
|
else
|
||||||
|
MSG="❌ pwap/web ${{ env.DOMAIN }}: deploy ($NEW_SHA) failed. Auto-rollback was not attempted. Manual: gh workflow run quality-gate.yml -f rollback_to=$PREV_SHA"
|
||||||
|
fi
|
||||||
curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
|
curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
|
||||||
-d "chat_id=${CEO_CHAT_ID}" \
|
-d "chat_id=${CEO_CHAT_ID}" --data-urlencode "text=$MSG"
|
||||||
-d "text=❌ pwap/web deploy FAILED: ${{ env.DOMAIN }} (sha ${{ steps.sha.outputs.sha }}). Health check did not pass after deploy. Manual rollback needed: gh workflow run quality-gate.yml -f rollback_to=<previous-sha>"
|
|
||||||
|
|
||||||
# ========================================
|
# ========================================
|
||||||
# DEPLOY TO pex.mom (VPS3 — geo-redundant mirror)
|
# DEPLOY TO pex.mom (VPS3 — geo-redundant mirror)
|
||||||
# ========================================
|
# ========================================
|
||||||
deploy-pex:
|
deploy-pex:
|
||||||
name: Deploy pex.mom
|
name: Deploy pex.mom
|
||||||
runs-on: ubuntu-latest
|
runs-on: pwap-runner
|
||||||
needs: [telegram-gate, bump-version, build-image]
|
needs: [telegram-gate, bump-version, build-image]
|
||||||
if: |
|
if: |
|
||||||
always() &&
|
always() &&
|
||||||
@@ -373,6 +474,13 @@ jobs:
|
|||||||
echo "sha=${{ needs.build-image.outputs.image_sha }}" >> $GITHUB_OUTPUT
|
echo "sha=${{ needs.build-image.outputs.image_sha }}" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Capture currently-live SHA (for auto-rollback)
|
||||||
|
id: prev
|
||||||
|
run: |
|
||||||
|
PREV=$(curl -sf --max-time 5 "https://${{ env.DOMAIN }}/.deploy-sha" | head -c 40 | tr -dc 'a-f0-9' || echo "")
|
||||||
|
echo "Previous live SHA: ${PREV:-unknown}"
|
||||||
|
echo "prev=$PREV" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Log in to GHCR
|
- name: Log in to GHCR
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
@@ -380,6 +488,23 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install cosign (for verify)
|
||||||
|
uses: sigstore/cosign-installer@v3
|
||||||
|
with:
|
||||||
|
cosign-release: 'v2.4.1'
|
||||||
|
|
||||||
|
- name: Verify image signature (cosign keyless)
|
||||||
|
env:
|
||||||
|
COSIGN_EXPERIMENTAL: '1'
|
||||||
|
run: |
|
||||||
|
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.sha.outputs.sha }}"
|
||||||
|
echo "Verifying signature for $IMAGE"
|
||||||
|
cosign verify "$IMAGE" \
|
||||||
|
--certificate-identity-regexp "^https://github.com/pezkuwichain/pwap/.github/workflows/quality-gate.yml@" \
|
||||||
|
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
|
||||||
|
> /dev/null
|
||||||
|
echo "✅ Signature valid — image was built by trusted CI"
|
||||||
|
|
||||||
- name: Extract /dist from image
|
- name: Extract /dist from image
|
||||||
run: |
|
run: |
|
||||||
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.sha.outputs.sha }}"
|
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.sha.outputs.sha }}"
|
||||||
@@ -388,6 +513,7 @@ jobs:
|
|||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
docker cp "$CID:/dist/." dist/
|
docker cp "$CID:/dist/." dist/
|
||||||
docker rm "$CID" >/dev/null
|
docker rm "$CID" >/dev/null
|
||||||
|
echo "${{ steps.sha.outputs.sha }}" > dist/.deploy-sha
|
||||||
|
|
||||||
- name: Deploy to VPS3
|
- name: Deploy to VPS3
|
||||||
uses: appleboy/scp-action@v1.0.0
|
uses: appleboy/scp-action@v1.0.0
|
||||||
@@ -401,6 +527,7 @@ jobs:
|
|||||||
strip_components: 1
|
strip_components: 1
|
||||||
|
|
||||||
- name: Health check (60s window)
|
- name: Health check (60s window)
|
||||||
|
id: healthcheck
|
||||||
run: |
|
run: |
|
||||||
for i in 1 2 3 4 5 6; do
|
for i in 1 2 3 4 5 6; do
|
||||||
if curl -sf --max-time 10 "https://${{ env.DOMAIN }}/" >/dev/null; then
|
if curl -sf --max-time 10 "https://${{ env.DOMAIN }}/" >/dev/null; then
|
||||||
@@ -413,6 +540,47 @@ jobs:
|
|||||||
echo "❌ Health check failed for ${{ env.DOMAIN }}"
|
echo "❌ Health check failed for ${{ env.DOMAIN }}"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
|
- name: Auto-rollback to previous SHA
|
||||||
|
id: rollback
|
||||||
|
if: failure() && steps.healthcheck.conclusion == 'failure' && steps.prev.outputs.prev != ''
|
||||||
|
run: |
|
||||||
|
PREV="${{ steps.prev.outputs.prev }}"
|
||||||
|
echo "🔄 Rolling back ${{ env.DOMAIN }} to $PREV"
|
||||||
|
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:$PREV"
|
||||||
|
docker pull "$IMAGE"
|
||||||
|
CID=$(docker create "$IMAGE")
|
||||||
|
rm -rf dist && mkdir dist
|
||||||
|
docker cp "$CID:/dist/." dist/
|
||||||
|
docker rm "$CID" >/dev/null
|
||||||
|
echo "$PREV" > dist/.deploy-sha
|
||||||
|
echo "rollback_sha=$PREV" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: SCP rollback artifact
|
||||||
|
if: steps.rollback.outcome == 'success'
|
||||||
|
uses: appleboy/scp-action@v1.0.0
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.VPS_PEX_HOST }}
|
||||||
|
username: ${{ secrets.VPS_PEX_USER }}
|
||||||
|
key: ${{ secrets.VPS_PEX_SSH_KEY }}
|
||||||
|
port: ${{ secrets.VPS_PEX_SSH_PORT || 22 }}
|
||||||
|
source: 'dist/*'
|
||||||
|
target: '/var/www/pex.mom'
|
||||||
|
strip_components: 1
|
||||||
|
|
||||||
|
- name: Re-health-check after rollback
|
||||||
|
if: steps.rollback.outcome == 'success'
|
||||||
|
id: healthcheck_rb
|
||||||
|
run: |
|
||||||
|
for i in 1 2 3 4 5 6; do
|
||||||
|
if curl -sf --max-time 10 "https://${{ env.DOMAIN }}/" >/dev/null; then
|
||||||
|
echo "✅ Rolled back successfully — ${{ env.DOMAIN }} healthy on ${{ steps.rollback.outputs.rollback_sha }}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
echo "❌ Rollback also failed!"
|
||||||
|
exit 1
|
||||||
|
|
||||||
- name: Post-deploy notification
|
- name: Post-deploy notification
|
||||||
if: success()
|
if: success()
|
||||||
run: |
|
run: |
|
||||||
@@ -423,10 +591,22 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
BOT_TOKEN: ${{ secrets.PEXSEC_BOT_TOKEN }}
|
BOT_TOKEN: ${{ secrets.PEXSEC_BOT_TOKEN }}
|
||||||
CEO_CHAT_ID: ${{ secrets.TELEGRAM_CEO_CHAT_ID }}
|
CEO_CHAT_ID: ${{ secrets.TELEGRAM_CEO_CHAT_ID }}
|
||||||
|
NEW_SHA: ${{ steps.sha.outputs.sha }}
|
||||||
|
PREV_SHA: ${{ steps.prev.outputs.prev }}
|
||||||
|
ROLLBACK_OUTCOME: ${{ steps.rollback.outcome }}
|
||||||
|
RECHECK_OUTCOME: ${{ steps.healthcheck_rb.outcome }}
|
||||||
run: |
|
run: |
|
||||||
|
if [ "$RECHECK_OUTCOME" = "success" ]; then
|
||||||
|
MSG="⚠️ pwap/web ${{ env.DOMAIN }}: deploy ($NEW_SHA) failed health check, AUTO-ROLLED-BACK to $PREV_SHA. Site healthy."
|
||||||
|
elif [ "$ROLLBACK_OUTCOME" = "success" ]; then
|
||||||
|
MSG="🚨 pwap/web ${{ env.DOMAIN }}: deploy ($NEW_SHA) failed AND rollback to $PREV_SHA also failed. Manual intervention needed."
|
||||||
|
elif [ -z "$PREV_SHA" ]; then
|
||||||
|
MSG="❌ pwap/web ${{ env.DOMAIN }}: deploy ($NEW_SHA) failed. No previous SHA available (first deploy?). Manual rollback: gh workflow run quality-gate.yml -f rollback_to=<sha>"
|
||||||
|
else
|
||||||
|
MSG="❌ pwap/web ${{ env.DOMAIN }}: deploy ($NEW_SHA) failed. Auto-rollback was not attempted. Manual: gh workflow run quality-gate.yml -f rollback_to=$PREV_SHA"
|
||||||
|
fi
|
||||||
curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
|
curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
|
||||||
-d "chat_id=${CEO_CHAT_ID}" \
|
-d "chat_id=${CEO_CHAT_ID}" --data-urlencode "text=$MSG"
|
||||||
-d "text=❌ pwap/web deploy FAILED: ${{ env.DOMAIN }} (sha ${{ steps.sha.outputs.sha }}). Health check did not pass. Rollback: gh workflow run quality-gate.yml -f rollback_to=<previous-sha>"
|
|
||||||
|
|
||||||
# ========================================
|
# ========================================
|
||||||
# SECURITY CHECKS (BLOCKING)
|
# SECURITY CHECKS (BLOCKING)
|
||||||
@@ -434,7 +614,7 @@ jobs:
|
|||||||
# ========================================
|
# ========================================
|
||||||
security-audit:
|
security-audit:
|
||||||
name: Security Audit
|
name: Security Audit
|
||||||
runs-on: ubuntu-latest
|
runs-on: pwap-runner
|
||||||
needs: [web]
|
needs: [web]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -448,11 +628,14 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: '20'
|
node-version: '20'
|
||||||
|
|
||||||
- name: Web — npm audit (high + critical)
|
- name: Web — npm audit (high + critical, production deps only)
|
||||||
working-directory: ./web
|
working-directory: ./web
|
||||||
run: |
|
run: |
|
||||||
npm install
|
npm install
|
||||||
npm audit --audit-level=high
|
# Audit only production dependencies. Build tooling (vite, esbuild,
|
||||||
|
# vite-plugin-node-polyfills → elliptic, etc.) ships to no user, and
|
||||||
|
# advisories on those dev deps kept blocking production deploys.
|
||||||
|
npm audit --audit-level=high --omit=dev
|
||||||
|
|
||||||
- name: TruffleHog — PR diff (verified secrets only)
|
- name: TruffleHog — PR diff (verified secrets only)
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
@@ -476,7 +659,7 @@ jobs:
|
|||||||
# ========================================
|
# ========================================
|
||||||
ci-gate:
|
ci-gate:
|
||||||
name: CI Gate ✅
|
name: CI Gate ✅
|
||||||
runs-on: ubuntu-latest
|
runs-on: pwap-runner
|
||||||
needs: [web, security-audit]
|
needs: [web, security-audit]
|
||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
|
|||||||
@@ -162,3 +162,11 @@ mobile/credentials.json
|
|||||||
|
|
||||||
# Backup files
|
# Backup files
|
||||||
*.bak
|
*.bak
|
||||||
|
|
||||||
|
# Generated docs artifacts — the in-app /docs route now redirects to the
|
||||||
|
# external docs.pezkuwichain.io (see web/src/pages/Docs.tsx, since c56e021a).
|
||||||
|
# These are (re)built from Pezkuwi-SDK by web/generate-docs-structure.cjs on
|
||||||
|
# pre(dev|build); they are NOT served at runtime, so we don't track them.
|
||||||
|
/web/public/docs/
|
||||||
|
/web/public/sdk_docs/
|
||||||
|
/web/public/docs-structure.json
|
||||||
|
|||||||
|
After Width: | Height: | Size: 2.1 MiB |
@@ -46,7 +46,7 @@ RUN npm run build
|
|||||||
# if the image were ever exposed.
|
# if the image were ever exposed.
|
||||||
FROM busybox:musl
|
FROM busybox:musl
|
||||||
WORKDIR /dist
|
WORKDIR /dist
|
||||||
COPY --from=builder /build/dist /dist
|
COPY --from=builder /build/web/dist /dist
|
||||||
LABEL org.opencontainers.image.source="https://github.com/pezkuwichain/pwap"
|
LABEL org.opencontainers.image.source="https://github.com/pezkuwichain/pwap"
|
||||||
LABEL org.opencontainers.image.description="pwap/web static SPA — Pezkuwi wallet/exchange frontend"
|
LABEL org.opencontainers.image.description="pwap/web static SPA — Pezkuwi wallet/exchange frontend"
|
||||||
LABEL org.opencontainers.image.licenses="proprietary"
|
LABEL org.opencontainers.image.licenses="proprietary"
|
||||||
|
|||||||
@@ -109,6 +109,7 @@
|
|||||||
"typescript-eslint": "^8.0.1",
|
"typescript-eslint": "^8.0.1",
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
"vite-plugin-node-polyfills": "^0.25.0",
|
"vite-plugin-node-polyfills": "^0.25.0",
|
||||||
|
"vite-plugin-subresource-integrity": "^0.0.12",
|
||||||
"vitest": "^4.0.10"
|
"vitest": "^4.0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -3507,9 +3508,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@remix-run/router": {
|
"node_modules/@remix-run/router": {
|
||||||
"version": "1.23.2",
|
"version": "1.23.3",
|
||||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
|
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.3.tgz",
|
||||||
"integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==",
|
"integrity": "sha512-4An71tdz9X8+3sI4Qqqd2LWd9vS39J7sqd9EU4Scw7TJE/qB10Flv/UuqbPVgfQV9XoK8Np6jNquZitnZq5i+Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
@@ -5070,31 +5071,31 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/expect": {
|
"node_modules/@vitest/expect": {
|
||||||
"version": "4.0.18",
|
"version": "4.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.8.tgz",
|
||||||
"integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==",
|
"integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@standard-schema/spec": "^1.0.0",
|
"@standard-schema/spec": "^1.1.0",
|
||||||
"@types/chai": "^5.2.2",
|
"@types/chai": "^5.2.2",
|
||||||
"@vitest/spy": "4.0.18",
|
"@vitest/spy": "4.1.8",
|
||||||
"@vitest/utils": "4.0.18",
|
"@vitest/utils": "4.1.8",
|
||||||
"chai": "^6.2.1",
|
"chai": "^6.2.2",
|
||||||
"tinyrainbow": "^3.0.3"
|
"tinyrainbow": "^3.1.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/vitest"
|
"url": "https://opencollective.com/vitest"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/mocker": {
|
"node_modules/@vitest/mocker": {
|
||||||
"version": "4.0.18",
|
"version": "4.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz",
|
||||||
"integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==",
|
"integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/spy": "4.0.18",
|
"@vitest/spy": "4.1.8",
|
||||||
"estree-walker": "^3.0.3",
|
"estree-walker": "^3.0.3",
|
||||||
"magic-string": "^0.30.21"
|
"magic-string": "^0.30.21"
|
||||||
},
|
},
|
||||||
@@ -5103,7 +5104,7 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"msw": "^2.4.9",
|
"msw": "^2.4.9",
|
||||||
"vite": "^6.0.0 || ^7.0.0-0"
|
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"msw": {
|
"msw": {
|
||||||
@@ -5125,26 +5126,26 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/pretty-format": {
|
"node_modules/@vitest/pretty-format": {
|
||||||
"version": "4.0.18",
|
"version": "4.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz",
|
||||||
"integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==",
|
"integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tinyrainbow": "^3.0.3"
|
"tinyrainbow": "^3.1.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/vitest"
|
"url": "https://opencollective.com/vitest"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/runner": {
|
"node_modules/@vitest/runner": {
|
||||||
"version": "4.0.18",
|
"version": "4.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.8.tgz",
|
||||||
"integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==",
|
"integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/utils": "4.0.18",
|
"@vitest/utils": "4.1.8",
|
||||||
"pathe": "^2.0.3"
|
"pathe": "^2.0.3"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -5152,13 +5153,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/snapshot": {
|
"node_modules/@vitest/snapshot": {
|
||||||
"version": "4.0.18",
|
"version": "4.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz",
|
||||||
"integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==",
|
"integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/pretty-format": "4.0.18",
|
"@vitest/pretty-format": "4.1.8",
|
||||||
|
"@vitest/utils": "4.1.8",
|
||||||
"magic-string": "^0.30.21",
|
"magic-string": "^0.30.21",
|
||||||
"pathe": "^2.0.3"
|
"pathe": "^2.0.3"
|
||||||
},
|
},
|
||||||
@@ -5167,9 +5169,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/spy": {
|
"node_modules/@vitest/spy": {
|
||||||
"version": "4.0.18",
|
"version": "4.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.8.tgz",
|
||||||
"integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==",
|
"integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -5177,14 +5179,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/utils": {
|
"node_modules/@vitest/utils": {
|
||||||
"version": "4.0.18",
|
"version": "4.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz",
|
||||||
"integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==",
|
"integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/pretty-format": "4.0.18",
|
"@vitest/pretty-format": "4.1.8",
|
||||||
"tinyrainbow": "^3.0.3"
|
"convert-source-map": "^2.0.0",
|
||||||
|
"tinyrainbow": "^3.1.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/vitest"
|
"url": "https://opencollective.com/vitest"
|
||||||
@@ -5454,9 +5457,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@walletconnect/jsonrpc-ws-connection/node_modules/ws": {
|
"node_modules/@walletconnect/jsonrpc-ws-connection/node_modules/ws": {
|
||||||
"version": "7.5.10",
|
"version": "7.5.11",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz",
|
||||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
"integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.3.0"
|
"node": ">=8.3.0"
|
||||||
@@ -6567,13 +6570,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/browserify-sign": {
|
"node_modules/browserify-sign": {
|
||||||
"version": "4.2.5",
|
"version": "4.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.6.tgz",
|
||||||
"integrity": "sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==",
|
"integrity": "sha512-sd+Q65fjlWCYWtZKXiKfrUc8d+4jtp/8f0W2NkwzLtoW4bI6UDnWusLWIurHnmurW0XShIRxpwiOX4EoPtXUAg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bn.js": "^5.2.2",
|
"bn.js": "^5.2.3",
|
||||||
"browserify-rsa": "^4.1.1",
|
"browserify-rsa": "^4.1.1",
|
||||||
"create-hash": "^1.2.0",
|
"create-hash": "^1.2.0",
|
||||||
"create-hmac": "^1.1.7",
|
"create-hmac": "^1.1.7",
|
||||||
@@ -7028,6 +7031,13 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/convert-source-map": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/cookie-es": {
|
"node_modules/cookie-es": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz",
|
||||||
@@ -7641,9 +7651,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dompurify": {
|
"node_modules/dompurify": {
|
||||||
"version": "3.4.2",
|
"version": "3.4.10",
|
||||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.10.tgz",
|
||||||
"integrity": "sha512-lHeS9SA/IKeIFFyYciHBr2n0v1VMPlSj843HdLOwjb2OxNwdq9Xykxqhk+FE42MzAdHvInbAolSE4mhahPpjXA==",
|
"integrity": "sha512-0xzNv0e7oYC6yyuOGZIABPM4qtg3QxLFniDNPP4ZP90wR8Yq3zgwpRbrNiT4N3IKqDbbYFEJLV+JWEs19aZ//w==",
|
||||||
"license": "(MPL-2.0 OR Apache-2.0)",
|
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@types/trusted-types": "^2.0.7"
|
"@types/trusted-types": "^2.0.7"
|
||||||
@@ -7859,9 +7869,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/es-module-lexer": {
|
"node_modules/es-module-lexer": {
|
||||||
"version": "1.7.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz",
|
||||||
"integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
|
"integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -11147,9 +11157,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.15.1",
|
"version": "6.15.2",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz",
|
||||||
"integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==",
|
"integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -11367,12 +11377,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router": {
|
"node_modules/react-router": {
|
||||||
"version": "6.30.3",
|
"version": "6.30.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.4.tgz",
|
||||||
"integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==",
|
"integrity": "sha512-SVUsDe+DybHM/WmYKIVYhZh1o5Dcuf16yM6WjG02Q9XVFMZIJyHYhwrr6bFBXZkVP6z69kNkMyBCujt8FaFLJA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@remix-run/router": "1.23.2"
|
"@remix-run/router": "1.23.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
@@ -11382,13 +11392,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router-dom": {
|
"node_modules/react-router-dom": {
|
||||||
"version": "6.30.3",
|
"version": "6.30.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.4.tgz",
|
||||||
"integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==",
|
"integrity": "sha512-q4HvNl+mmDdkS0g+MqiBZNteQJCuimWoOyHMy4T/RQLAn9Z29+E91QXRaxOujeMl2HTzRSS0KFPd7lxX3PjV0Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@remix-run/router": "1.23.2",
|
"@remix-run/router": "1.23.3",
|
||||||
"react-router": "6.30.3"
|
"react-router": "6.30.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
@@ -12237,9 +12247,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/std-env": {
|
"node_modules/std-env": {
|
||||||
"version": "3.10.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz",
|
||||||
"integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
|
"integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -12696,9 +12706,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tinyrainbow": {
|
"node_modules/tinyrainbow": {
|
||||||
"version": "3.0.3",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz",
|
||||||
"integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==",
|
"integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -13259,6 +13269,13 @@
|
|||||||
"vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
"vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vite-plugin-subresource-integrity": {
|
||||||
|
"version": "0.0.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/vite-plugin-subresource-integrity/-/vite-plugin-subresource-integrity-0.0.12.tgz",
|
||||||
|
"integrity": "sha512-geKEo1KgGA56G8CciaoKA3Yf7ckpR23zSuSW802xrisW6vnH+dAYjKXZygEcmFKfsOe0+r3uG7Oz9eFEwhdjxg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/vite/node_modules/fdir": {
|
"node_modules/vite/node_modules/fdir": {
|
||||||
"version": "6.5.0",
|
"version": "6.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||||
@@ -13291,31 +13308,31 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vitest": {
|
"node_modules/vitest": {
|
||||||
"version": "4.0.18",
|
"version": "4.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz",
|
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.8.tgz",
|
||||||
"integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==",
|
"integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/expect": "4.0.18",
|
"@vitest/expect": "4.1.8",
|
||||||
"@vitest/mocker": "4.0.18",
|
"@vitest/mocker": "4.1.8",
|
||||||
"@vitest/pretty-format": "4.0.18",
|
"@vitest/pretty-format": "4.1.8",
|
||||||
"@vitest/runner": "4.0.18",
|
"@vitest/runner": "4.1.8",
|
||||||
"@vitest/snapshot": "4.0.18",
|
"@vitest/snapshot": "4.1.8",
|
||||||
"@vitest/spy": "4.0.18",
|
"@vitest/spy": "4.1.8",
|
||||||
"@vitest/utils": "4.0.18",
|
"@vitest/utils": "4.1.8",
|
||||||
"es-module-lexer": "^1.7.0",
|
"es-module-lexer": "^2.0.0",
|
||||||
"expect-type": "^1.2.2",
|
"expect-type": "^1.3.0",
|
||||||
"magic-string": "^0.30.21",
|
"magic-string": "^0.30.21",
|
||||||
"obug": "^2.1.1",
|
"obug": "^2.1.1",
|
||||||
"pathe": "^2.0.3",
|
"pathe": "^2.0.3",
|
||||||
"picomatch": "^4.0.3",
|
"picomatch": "^4.0.3",
|
||||||
"std-env": "^3.10.0",
|
"std-env": "^4.0.0-rc.1",
|
||||||
"tinybench": "^2.9.0",
|
"tinybench": "^2.9.0",
|
||||||
"tinyexec": "^1.0.2",
|
"tinyexec": "^1.0.2",
|
||||||
"tinyglobby": "^0.2.15",
|
"tinyglobby": "^0.2.15",
|
||||||
"tinyrainbow": "^3.0.3",
|
"tinyrainbow": "^3.1.0",
|
||||||
"vite": "^6.0.0 || ^7.0.0",
|
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
|
||||||
"why-is-node-running": "^2.3.0"
|
"why-is-node-running": "^2.3.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -13331,12 +13348,15 @@
|
|||||||
"@edge-runtime/vm": "*",
|
"@edge-runtime/vm": "*",
|
||||||
"@opentelemetry/api": "^1.9.0",
|
"@opentelemetry/api": "^1.9.0",
|
||||||
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
|
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
|
||||||
"@vitest/browser-playwright": "4.0.18",
|
"@vitest/browser-playwright": "4.1.8",
|
||||||
"@vitest/browser-preview": "4.0.18",
|
"@vitest/browser-preview": "4.1.8",
|
||||||
"@vitest/browser-webdriverio": "4.0.18",
|
"@vitest/browser-webdriverio": "4.1.8",
|
||||||
"@vitest/ui": "4.0.18",
|
"@vitest/coverage-istanbul": "4.1.8",
|
||||||
|
"@vitest/coverage-v8": "4.1.8",
|
||||||
|
"@vitest/ui": "4.1.8",
|
||||||
"happy-dom": "*",
|
"happy-dom": "*",
|
||||||
"jsdom": "*"
|
"jsdom": "*",
|
||||||
|
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@edge-runtime/vm": {
|
"@edge-runtime/vm": {
|
||||||
@@ -13357,6 +13377,12 @@
|
|||||||
"@vitest/browser-webdriverio": {
|
"@vitest/browser-webdriverio": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"@vitest/coverage-istanbul": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@vitest/coverage-v8": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"@vitest/ui": {
|
"@vitest/ui": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
@@ -13365,6 +13391,9 @@
|
|||||||
},
|
},
|
||||||
"jsdom": {
|
"jsdom": {
|
||||||
"optional": true
|
"optional": true
|
||||||
|
},
|
||||||
|
"vite": {
|
||||||
|
"optional": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -13606,9 +13635,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "8.19.0",
|
"version": "8.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
|
||||||
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
|
"integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
|
|||||||
@@ -120,7 +120,8 @@
|
|||||||
"@pezkuwi/x-textdecoder": "^14.0.25",
|
"@pezkuwi/x-textdecoder": "^14.0.25",
|
||||||
"@pezkuwi/x-textencoder": "^14.0.25",
|
"@pezkuwi/x-textencoder": "^14.0.25",
|
||||||
"@pezkuwi/x-ws": "^14.0.25",
|
"@pezkuwi/x-ws": "^14.0.25",
|
||||||
"@pezkuwi/networks": "^14.0.25"
|
"@pezkuwi/networks": "^14.0.25",
|
||||||
|
"elliptic": "^6.6.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.9.0",
|
"@eslint/js": "^9.9.0",
|
||||||
@@ -147,6 +148,7 @@
|
|||||||
"typescript-eslint": "^8.0.1",
|
"typescript-eslint": "^8.0.1",
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
"vite-plugin-node-polyfills": "^0.25.0",
|
"vite-plugin-node-polyfills": "^0.25.0",
|
||||||
|
"vite-plugin-subresource-integrity": "^0.0.12",
|
||||||
"vitest": "^4.0.10"
|
"vitest": "^4.0.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"Getting Started": {
|
|
||||||
"Introduction": "introduction.md",
|
|
||||||
"Whitepaper": "whitepaper/whitepaper.md"
|
|
||||||
},
|
|
||||||
"SDK Reference": {
|
|
||||||
"📚 Rust SDK Docs": "sdk://open",
|
|
||||||
"Runtimes & Pallets": "runtimes-pallets.md"
|
|
||||||
},
|
|
||||||
"General Docs": {
|
|
||||||
"AUDIT": "AUDIT.md",
|
|
||||||
"BACKPORT": "BACKPORT.md",
|
|
||||||
"RELEASE": "RELEASE.md"
|
|
||||||
},
|
|
||||||
"Contributor": {
|
|
||||||
"CODE OF CONDUCT": "contributor/CODE_OF_CONDUCT.md",
|
|
||||||
"Commands Readme": "contributor/commands-readme.md",
|
|
||||||
"Container": "contributor/container.md",
|
|
||||||
"CONTRIBUTING": "contributor/CONTRIBUTING.md",
|
|
||||||
"DEPRECATION CHECKLIST": "contributor/DEPRECATION_CHECKLIST.md",
|
|
||||||
"Docker": "contributor/docker.md",
|
|
||||||
"DOCUMENTATION GUIDELINES": "contributor/DOCUMENTATION_GUIDELINES.md",
|
|
||||||
"Markdown Linting": "contributor/markdown_linting.md",
|
|
||||||
"Prdoc": "contributor/prdoc.md",
|
|
||||||
"PULL REQUEST TEMPLATE": "contributor/PULL_REQUEST_TEMPLATE.md",
|
|
||||||
"SECURITY": "contributor/SECURITY.md",
|
|
||||||
"STYLE GUIDE": "contributor/STYLE_GUIDE.md",
|
|
||||||
"Weight Generation": "contributor/weight-generation.md"
|
|
||||||
},
|
|
||||||
"README": "README.md"
|
|
||||||
}
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
|
||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
*.lcov
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# Snowpack dependency directory (https://snowpack.dev/)
|
|
||||||
web_modules/
|
|
||||||
|
|
||||||
# TypeScript cache
|
|
||||||
*.tsbuildinfo
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Optional stylelint cache
|
|
||||||
.stylelintcache
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variable files
|
|
||||||
.env
|
|
||||||
.env.*
|
|
||||||
!.env.example
|
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
|
||||||
.cache
|
|
||||||
.parcel-cache
|
|
||||||
|
|
||||||
# Next.js build output
|
|
||||||
.next
|
|
||||||
out
|
|
||||||
|
|
||||||
# Nuxt.js build / generate output
|
|
||||||
.nuxt
|
|
||||||
dist
|
|
||||||
|
|
||||||
# Gatsby files
|
|
||||||
.cache/
|
|
||||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
|
||||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
|
||||||
# public
|
|
||||||
|
|
||||||
# vuepress build output
|
|
||||||
.vuepress/dist
|
|
||||||
|
|
||||||
# vuepress v2.x temp and cache directory
|
|
||||||
.temp
|
|
||||||
.cache
|
|
||||||
|
|
||||||
# Sveltekit cache directory
|
|
||||||
.svelte-kit/
|
|
||||||
|
|
||||||
# vitepress build output
|
|
||||||
**/.vitepress/dist
|
|
||||||
|
|
||||||
# vitepress cache directory
|
|
||||||
**/.vitepress/cache
|
|
||||||
|
|
||||||
# Docusaurus cache and generated files
|
|
||||||
.docusaurus
|
|
||||||
|
|
||||||
# Serverless directories
|
|
||||||
.serverless/
|
|
||||||
|
|
||||||
# FuseBox cache
|
|
||||||
.fusebox/
|
|
||||||
|
|
||||||
# DynamoDB Local files
|
|
||||||
.dynamodb/
|
|
||||||
|
|
||||||
# Firebase cache directory
|
|
||||||
.firebase/
|
|
||||||
|
|
||||||
# TernJS port file
|
|
||||||
.tern-port
|
|
||||||
|
|
||||||
# Stores VSCode versions used for testing VSCode extensions
|
|
||||||
.vscode-test
|
|
||||||
|
|
||||||
# yarn v3
|
|
||||||
.pnp.*
|
|
||||||
.yarn/*
|
|
||||||
!.yarn/patches
|
|
||||||
!.yarn/plugins
|
|
||||||
!.yarn/releases
|
|
||||||
!.yarn/sdks
|
|
||||||
!.yarn/versions
|
|
||||||
|
|
||||||
# Vite logs files
|
|
||||||
vite.config.js.timestamp-*
|
|
||||||
vite.config.ts.timestamp-*
|
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# docs.pezkuwichain.io
|
|
||||||
A sovereign blockchain parachain built for the Kurdish Nation and Culturel Nations of the world, on blockchain
|
|
||||||
|
Before Width: | Height: | Size: 634 KiB |
|
Before Width: | Height: | Size: 5.0 MiB |
|
Before Width: | Height: | Size: 5.0 MiB |
|
Before Width: | Height: | Size: 601 KiB |
|
Before Width: | Height: | Size: 208 KiB |
|
Before Width: | Height: | Size: 208 KiB |
|
Before Width: | Height: | Size: 355 KiB |
|
Before Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 586 KiB |
|
Before Width: | Height: | Size: 742 KiB |
|
Before Width: | Height: | Size: 750 KiB |
|
Before Width: | Height: | Size: 269 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 265 KiB |
|
Before Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 178 KiB |
|
Before Width: | Height: | Size: 178 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 429 KiB |
@@ -1,12 +0,0 @@
|
|||||||
flowchart TD
|
|
||||||
dot[pezkuwichain.io] --> devhub[pezkuwi_sdk_docs]
|
|
||||||
|
|
||||||
devhub --> pezkuwi_sdk
|
|
||||||
devhub --> reference_docs
|
|
||||||
devhub --> guides
|
|
||||||
devhub --> external_resources
|
|
||||||
|
|
||||||
pezkuwi_sdk --> bizinikiwi
|
|
||||||
pezkuwi_sdk --> frame
|
|
||||||
pezkuwi_sdk --> xcm
|
|
||||||
pezkuwi_sdk --> templates
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
graph TB
|
|
||||||
subgraph Bizinikiwi
|
|
||||||
direction LR
|
|
||||||
subgraph Node
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph Runtime
|
|
||||||
end
|
|
||||||
|
|
||||||
Node --runtime-api--> Runtime
|
|
||||||
Runtime --host-functions--> Node
|
|
||||||
end
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
flowchart LR
|
|
||||||
T[Using a Template] --> P[Writing Your Own FRAME-Based Pallet] --> C[Custom Node]
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
graph TB
|
|
||||||
subgraph Bizinikiwi
|
|
||||||
direction LR
|
|
||||||
subgraph Node
|
|
||||||
end
|
|
||||||
subgraph Runtime
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
graph TB
|
|
||||||
subgraph Bizinikiwi
|
|
||||||
direction LR
|
|
||||||
subgraph Node
|
|
||||||
Database
|
|
||||||
Networking
|
|
||||||
Consensus
|
|
||||||
end
|
|
||||||
subgraph Runtime
|
|
||||||
subgraph FRAME
|
|
||||||
direction LR
|
|
||||||
Governance
|
|
||||||
Currency
|
|
||||||
Staking
|
|
||||||
Identity
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Node --runtime-api--> Runtime
|
|
||||||
Runtime --host-functions--> Node
|
|
||||||
end
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
flowchart TD
|
|
||||||
E(Extrinsic) ---> I(Inherent);
|
|
||||||
E --> T(Transaction)
|
|
||||||
T --> ST("Signed (aka. Transaction)")
|
|
||||||
T --> UT(Unsigned)
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
flowchart LR
|
|
||||||
RuntimeCall --"TryInto"--> PalletCall
|
|
||||||
PalletCall --"Into"--> RuntimeCall
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
flowchart LR
|
|
||||||
subgraph PezkuwiSDKChain[A Pezkuwi SDK-based blockchain]
|
|
||||||
Node
|
|
||||||
Runtime
|
|
||||||
end
|
|
||||||
|
|
||||||
FRAME -.-> Runtime
|
|
||||||
PezkuwiSDK[Pezkuwi SDK Node Libraries] -.-> Node
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
flowchart LR
|
|
||||||
|
|
||||||
subgraph Pezkuwi[The Pezkuwi Relay Chain]
|
|
||||||
PezkuwiNode[Pezkuwi Node]
|
|
||||||
PezkuwiRuntime[Pezkuwi Runtime]
|
|
||||||
end
|
|
||||||
|
|
||||||
FRAME -.-> PezkuwiRuntime
|
|
||||||
PezkuwiSDK[Pezkuwi SDK Node Libraries] -.-> PezkuwiNode
|
|
||||||
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
flowchart LR
|
|
||||||
subgraph TeyrChain[A Pezkuwi TeyrChain]
|
|
||||||
TeyrChainNode[TeyrChain Node]
|
|
||||||
TeyrChainRuntime[TeyrChain Runtime]
|
|
||||||
end
|
|
||||||
|
|
||||||
FRAME -.-> TeyrChainRuntime
|
|
||||||
PezkuwiSDK[Pezkuwi SDK Node Libraries] -.-> TeyrChainNode
|
|
||||||
|
|
||||||
CumulusC[Pezcumulus Node Libraries] -.-> TeyrChainNode
|
|
||||||
CumulusR[Pezcumulus Runtime Libraries] -.-> TeyrChainRuntime
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
flowchart TB
|
|
||||||
subgraph Node[Node's View Of The State 🙈]
|
|
||||||
direction LR
|
|
||||||
0x1234 --> 0x2345
|
|
||||||
0x3456 --> 0x4567
|
|
||||||
0x5678 --> 0x6789
|
|
||||||
:code --> code[wasm code]
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph Runtime[Runtime's View Of The State 🙉]
|
|
||||||
direction LR
|
|
||||||
ab[alice's balance] --> abv[known value]
|
|
||||||
bb[bob's balance] --> bbv[known value]
|
|
||||||
cb[charlie's balance] --> cbv[known value]
|
|
||||||
c2[:code] --> c22[wasm code]
|
|
||||||
end
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
flowchart LR
|
|
||||||
%%{init: {'flowchart' : {'curve' : 'linear'}}}%%
|
|
||||||
subgraph BData[Blockchain Database]
|
|
||||||
direction LR
|
|
||||||
BN[Block N] -.-> BN1[Block N+1]
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph SData[State Database]
|
|
||||||
direction LR
|
|
||||||
SN[State N] -.-> SN1[State N+1] -.-> SN2[State N+2]
|
|
||||||
end
|
|
||||||
|
|
||||||
BN --> STFN[STF]
|
|
||||||
SN --> STFN[STF]
|
|
||||||
STFN[STF] --> SN1
|
|
||||||
|
|
||||||
BN1 --> STFN1[STF]
|
|
||||||
SN1 --> STFN1[STF]
|
|
||||||
STFN1[STF] --> SN2
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
flowchart LR
|
|
||||||
B[Block] --> STF
|
|
||||||
S[State] --> STF
|
|
||||||
STF --> NS[New State]
|
|
||||||
@@ -1,322 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "pezkuwi-sdk-docs"
|
|
||||||
description = "The one stop shop for developers of the pezkuwi-sdk"
|
|
||||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
|
||||||
homepage = "https://docs.pezkuwichain.io/sdk/"
|
|
||||||
repository.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
# This crate is not publish-able to crates.io for now because of docify.
|
|
||||||
publish = false
|
|
||||||
version = "0.0.2"
|
|
||||||
documentation.workspace = true
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
# Needed for all FRAME-based code
|
|
||||||
codec = { workspace = true }
|
|
||||||
pezframe = { features = [
|
|
||||||
"experimental",
|
|
||||||
"runtime",
|
|
||||||
], workspace = true, default-features = true }
|
|
||||||
pezpallet-contracts = { workspace = true }
|
|
||||||
pezpallet-default-config-example = { workspace = true, default-features = true }
|
|
||||||
pezpallet-example-offchain-worker = { workspace = true, default-features = true }
|
|
||||||
pezpallet-examples = { workspace = true }
|
|
||||||
scale-info = { workspace = true }
|
|
||||||
|
|
||||||
# How we build docs in rust-docs
|
|
||||||
docify = { workspace = true }
|
|
||||||
serde_json = { workspace = true }
|
|
||||||
simple-mermaid = { workspace = true }
|
|
||||||
|
|
||||||
# Pezkuwi SDK deps, typically all should only be in scope such that we can link to their doc item.
|
|
||||||
chain-spec-builder = { workspace = true, default-features = true }
|
|
||||||
log = { workspace = true, default-features = true }
|
|
||||||
node-cli = { workspace = true }
|
|
||||||
pez-kitchensink-runtime = { workspace = true }
|
|
||||||
pez-subkey = { workspace = true, default-features = true }
|
|
||||||
pezframe-benchmarking = { workspace = true }
|
|
||||||
pezframe-executive = { workspace = true }
|
|
||||||
pezframe-metadata-hash-extension = { workspace = true, default-features = true }
|
|
||||||
pezframe-support = { workspace = true }
|
|
||||||
pezframe-system = { workspace = true }
|
|
||||||
pezkuwi-sdk = { features = [
|
|
||||||
"runtime-full",
|
|
||||||
], workspace = true, default-features = true }
|
|
||||||
pezpallet-example-authorization-tx-extension = { workspace = true, default-features = true }
|
|
||||||
pezpallet-example-single-block-migrations = { workspace = true, default-features = true }
|
|
||||||
|
|
||||||
# Bizinikiwi Client
|
|
||||||
pezsc-chain-spec = { workspace = true, default-features = true }
|
|
||||||
pezsc-cli = { workspace = true, default-features = true }
|
|
||||||
pezsc-client-db = { workspace = true, default-features = true }
|
|
||||||
pezsc-consensus-aura = { workspace = true, default-features = true }
|
|
||||||
pezsc-consensus-babe = { workspace = true, default-features = true }
|
|
||||||
pezsc-consensus-beefy = { workspace = true, default-features = true }
|
|
||||||
pezsc-consensus-grandpa = { workspace = true, default-features = true }
|
|
||||||
pezsc-consensus-manual-seal = { workspace = true, default-features = true }
|
|
||||||
pezsc-consensus-pow = { workspace = true, default-features = true }
|
|
||||||
pezsc-executor = { workspace = true, default-features = true }
|
|
||||||
pezsc-network = { workspace = true, default-features = true }
|
|
||||||
pezsc-rpc = { workspace = true, default-features = true }
|
|
||||||
pezsc-rpc-api = { workspace = true, default-features = true }
|
|
||||||
pezsc-service = { workspace = true, default-features = true }
|
|
||||||
|
|
||||||
bizinikiwi-wasm-builder = { workspace = true, default-features = true }
|
|
||||||
|
|
||||||
# Pezcumulus
|
|
||||||
pezcumulus-client-service = { workspace = true, default-features = true }
|
|
||||||
pezcumulus-pezpallet-aura-ext = { workspace = true, default-features = true }
|
|
||||||
pezcumulus-pezpallet-teyrchain-system = { workspace = true, default-features = true }
|
|
||||||
pezcumulus-pezpallet-weight-reclaim = { workspace = true, default-features = true }
|
|
||||||
pezcumulus-primitives-proof-size-hostfunction = { workspace = true, default-features = true }
|
|
||||||
teyrchain-info = { workspace = true, default-features = true }
|
|
||||||
|
|
||||||
# Omni Node
|
|
||||||
pezkuwi-omni-node-lib = { workspace = true, default-features = true }
|
|
||||||
|
|
||||||
# Pallets and FRAME internals
|
|
||||||
pezpallet-asset-conversion-tx-payment = { workspace = true, default-features = true }
|
|
||||||
pezpallet-asset-tx-payment = { workspace = true, default-features = true }
|
|
||||||
pezpallet-assets = { workspace = true, default-features = true }
|
|
||||||
pezpallet-aura = { workspace = true, default-features = true }
|
|
||||||
pezpallet-babe = { workspace = true, default-features = true }
|
|
||||||
pezpallet-balances = { workspace = true, default-features = true }
|
|
||||||
pezpallet-collective = { workspace = true, default-features = true }
|
|
||||||
pezpallet-democracy = { workspace = true, default-features = true }
|
|
||||||
pezpallet-grandpa = { workspace = true, default-features = true }
|
|
||||||
pezpallet-nfts = { workspace = true, default-features = true }
|
|
||||||
pezpallet-preimage = { workspace = true, default-features = true }
|
|
||||||
pezpallet-scheduler = { workspace = true, default-features = true }
|
|
||||||
pezpallet-skip-feeless-payment = { workspace = true, default-features = true }
|
|
||||||
pezpallet-timestamp = { workspace = true, default-features = true }
|
|
||||||
pezpallet-transaction-payment = { workspace = true, default-features = true }
|
|
||||||
pezpallet-uniques = { workspace = true, default-features = true }
|
|
||||||
|
|
||||||
# Primitives
|
|
||||||
pezsp-api = { workspace = true, default-features = true }
|
|
||||||
pezsp-arithmetic = { workspace = true, default-features = true }
|
|
||||||
pezsp-core = { workspace = true, default-features = true }
|
|
||||||
pezsp-genesis-builder = { workspace = true, default-features = true }
|
|
||||||
pezsp-io = { workspace = true, default-features = true }
|
|
||||||
pezsp-keyring = { workspace = true, default-features = true }
|
|
||||||
pezsp-offchain = { workspace = true, default-features = true }
|
|
||||||
pezsp-runtime = { workspace = true, default-features = true }
|
|
||||||
pezsp-runtime-interface = { workspace = true, default-features = true }
|
|
||||||
pezsp-std = { workspace = true, default-features = true }
|
|
||||||
pezsp-storage = { workspace = true, default-features = true }
|
|
||||||
pezsp-tracing = { workspace = true, default-features = true }
|
|
||||||
pezsp-version = { workspace = true, default-features = true }
|
|
||||||
pezsp-weights = { workspace = true, default-features = true }
|
|
||||||
|
|
||||||
# XCM
|
|
||||||
pezpallet-xcm = { workspace = true }
|
|
||||||
xcm = { workspace = true, default-features = true }
|
|
||||||
xcm-builder = { workspace = true }
|
|
||||||
xcm-executor = { workspace = true }
|
|
||||||
xcm-pez-docs = { workspace = true }
|
|
||||||
xcm-pez-simulator = { workspace = true }
|
|
||||||
|
|
||||||
# Runtime guides
|
|
||||||
pez-chain-spec-guide-runtime = { workspace = true, default-features = true }
|
|
||||||
|
|
||||||
# Templates
|
|
||||||
pez-minimal-template-runtime = { workspace = true, default-features = true }
|
|
||||||
pez-solochain-template-runtime = { workspace = true, default-features = true }
|
|
||||||
|
|
||||||
# local packages
|
|
||||||
first-runtime = { workspace = true, default-features = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
assert_cmd = { workspace = true }
|
|
||||||
cmd_lib = { workspace = true }
|
|
||||||
rand = { workspace = true, default-features = true }
|
|
||||||
tokio = { workspace = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
runtime-benchmarks = [
|
|
||||||
"bizinikiwi-wasm-builder/runtime-benchmarks",
|
|
||||||
"chain-spec-builder/runtime-benchmarks",
|
|
||||||
"first-runtime/runtime-benchmarks",
|
|
||||||
"node-cli/runtime-benchmarks",
|
|
||||||
"pez-chain-spec-guide-runtime/runtime-benchmarks",
|
|
||||||
"pez-kitchensink-runtime/runtime-benchmarks",
|
|
||||||
"pez-minimal-template-runtime/runtime-benchmarks",
|
|
||||||
"pez-solochain-template-runtime/runtime-benchmarks",
|
|
||||||
"pez-subkey/runtime-benchmarks",
|
|
||||||
"pezcumulus-client-service/runtime-benchmarks",
|
|
||||||
"pezcumulus-pezpallet-aura-ext/runtime-benchmarks",
|
|
||||||
"pezcumulus-pezpallet-teyrchain-system/runtime-benchmarks",
|
|
||||||
"pezcumulus-pezpallet-weight-reclaim/runtime-benchmarks",
|
|
||||||
"pezcumulus-primitives-proof-size-hostfunction/runtime-benchmarks",
|
|
||||||
"pezframe-benchmarking/runtime-benchmarks",
|
|
||||||
"pezframe-executive/runtime-benchmarks",
|
|
||||||
"pezframe-metadata-hash-extension/runtime-benchmarks",
|
|
||||||
"pezframe-support/runtime-benchmarks",
|
|
||||||
"pezframe-system/runtime-benchmarks",
|
|
||||||
"pezframe/runtime-benchmarks",
|
|
||||||
"pezkuwi-omni-node-lib/runtime-benchmarks",
|
|
||||||
"pezkuwi-sdk/runtime-benchmarks",
|
|
||||||
"pezpallet-asset-conversion-tx-payment/runtime-benchmarks",
|
|
||||||
"pezpallet-asset-tx-payment/runtime-benchmarks",
|
|
||||||
"pezpallet-assets/runtime-benchmarks",
|
|
||||||
"pezpallet-aura/runtime-benchmarks",
|
|
||||||
"pezpallet-babe/runtime-benchmarks",
|
|
||||||
"pezpallet-balances/runtime-benchmarks",
|
|
||||||
"pezpallet-collective/runtime-benchmarks",
|
|
||||||
"pezpallet-contracts/runtime-benchmarks",
|
|
||||||
"pezpallet-default-config-example/runtime-benchmarks",
|
|
||||||
"pezpallet-democracy/runtime-benchmarks",
|
|
||||||
"pezpallet-example-authorization-tx-extension/runtime-benchmarks",
|
|
||||||
"pezpallet-example-offchain-worker/runtime-benchmarks",
|
|
||||||
"pezpallet-example-single-block-migrations/runtime-benchmarks",
|
|
||||||
"pezpallet-examples/runtime-benchmarks",
|
|
||||||
"pezpallet-grandpa/runtime-benchmarks",
|
|
||||||
"pezpallet-nfts/runtime-benchmarks",
|
|
||||||
"pezpallet-preimage/runtime-benchmarks",
|
|
||||||
"pezpallet-scheduler/runtime-benchmarks",
|
|
||||||
"pezpallet-skip-feeless-payment/runtime-benchmarks",
|
|
||||||
"pezpallet-timestamp/runtime-benchmarks",
|
|
||||||
"pezpallet-transaction-payment/runtime-benchmarks",
|
|
||||||
"pezpallet-uniques/runtime-benchmarks",
|
|
||||||
"pezpallet-xcm/runtime-benchmarks",
|
|
||||||
"pezsc-chain-spec/runtime-benchmarks",
|
|
||||||
"pezsc-cli/runtime-benchmarks",
|
|
||||||
"pezsc-client-db/runtime-benchmarks",
|
|
||||||
"pezsc-consensus-aura/runtime-benchmarks",
|
|
||||||
"pezsc-consensus-babe/runtime-benchmarks",
|
|
||||||
"pezsc-consensus-beefy/runtime-benchmarks",
|
|
||||||
"pezsc-consensus-grandpa/runtime-benchmarks",
|
|
||||||
"pezsc-consensus-manual-seal/runtime-benchmarks",
|
|
||||||
"pezsc-consensus-pow/runtime-benchmarks",
|
|
||||||
"pezsc-executor/runtime-benchmarks",
|
|
||||||
"pezsc-network/runtime-benchmarks",
|
|
||||||
"pezsc-rpc-api/runtime-benchmarks",
|
|
||||||
"pezsc-rpc/runtime-benchmarks",
|
|
||||||
"pezsc-service/runtime-benchmarks",
|
|
||||||
"pezsp-api/runtime-benchmarks",
|
|
||||||
"pezsp-genesis-builder/runtime-benchmarks",
|
|
||||||
"pezsp-io/runtime-benchmarks",
|
|
||||||
"pezsp-keyring/runtime-benchmarks",
|
|
||||||
"pezsp-offchain/runtime-benchmarks",
|
|
||||||
"pezsp-runtime-interface/runtime-benchmarks",
|
|
||||||
"pezsp-runtime/runtime-benchmarks",
|
|
||||||
"pezsp-version/runtime-benchmarks",
|
|
||||||
"teyrchain-info/runtime-benchmarks",
|
|
||||||
"xcm-builder/runtime-benchmarks",
|
|
||||||
"xcm-executor/runtime-benchmarks",
|
|
||||||
"xcm-pez-docs/runtime-benchmarks",
|
|
||||||
"xcm-pez-simulator/runtime-benchmarks",
|
|
||||||
"xcm/runtime-benchmarks",
|
|
||||||
]
|
|
||||||
std = [
|
|
||||||
"bizinikiwi-wasm-builder/std",
|
|
||||||
"chain-spec-builder/std",
|
|
||||||
"codec/std",
|
|
||||||
"log/std",
|
|
||||||
"node-cli/std",
|
|
||||||
"pez-subkey/std",
|
|
||||||
"pezcumulus-client-service/std",
|
|
||||||
"pezframe-benchmarking/std",
|
|
||||||
"pezframe-executive/std",
|
|
||||||
"pezframe-support/std",
|
|
||||||
"pezframe-system/std",
|
|
||||||
"pezkuwi-omni-node-lib/std",
|
|
||||||
"pezpallet-contracts/std",
|
|
||||||
"pezpallet-xcm/std",
|
|
||||||
"pezsc-chain-spec/std",
|
|
||||||
"pezsc-cli/std",
|
|
||||||
"pezsc-client-db/std",
|
|
||||||
"pezsc-consensus-aura/std",
|
|
||||||
"pezsc-consensus-babe/std",
|
|
||||||
"pezsc-consensus-beefy/std",
|
|
||||||
"pezsc-consensus-grandpa/std",
|
|
||||||
"pezsc-consensus-manual-seal/std",
|
|
||||||
"pezsc-consensus-pow/std",
|
|
||||||
"pezsc-network/std",
|
|
||||||
"pezsc-rpc-api/std",
|
|
||||||
"pezsc-rpc/std",
|
|
||||||
"pezsc-service/std",
|
|
||||||
"scale-info/std",
|
|
||||||
"serde_json/std",
|
|
||||||
"xcm-builder/std",
|
|
||||||
"xcm-executor/std",
|
|
||||||
"xcm-pez-docs/std",
|
|
||||||
"xcm-pez-simulator/std",
|
|
||||||
]
|
|
||||||
try-runtime = [
|
|
||||||
"first-runtime/try-runtime",
|
|
||||||
"node-cli/try-runtime",
|
|
||||||
"pez-chain-spec-guide-runtime/try-runtime",
|
|
||||||
"pez-kitchensink-runtime/try-runtime",
|
|
||||||
"pez-minimal-template-runtime/try-runtime",
|
|
||||||
"pez-solochain-template-runtime/try-runtime",
|
|
||||||
"pezcumulus-client-service/try-runtime",
|
|
||||||
"pezcumulus-pezpallet-aura-ext/try-runtime",
|
|
||||||
"pezcumulus-pezpallet-teyrchain-system/try-runtime",
|
|
||||||
"pezcumulus-pezpallet-weight-reclaim/try-runtime",
|
|
||||||
"pezframe-benchmarking/try-runtime",
|
|
||||||
"pezframe-executive/try-runtime",
|
|
||||||
"pezframe-metadata-hash-extension/try-runtime",
|
|
||||||
"pezframe-support/try-runtime",
|
|
||||||
"pezframe-system/try-runtime",
|
|
||||||
"pezframe/try-runtime",
|
|
||||||
"pezkuwi-omni-node-lib/try-runtime",
|
|
||||||
"pezkuwi-sdk/try-runtime",
|
|
||||||
"pezpallet-asset-conversion-tx-payment/try-runtime",
|
|
||||||
"pezpallet-asset-tx-payment/try-runtime",
|
|
||||||
"pezpallet-assets/try-runtime",
|
|
||||||
"pezpallet-aura/try-runtime",
|
|
||||||
"pezpallet-babe/try-runtime",
|
|
||||||
"pezpallet-balances/try-runtime",
|
|
||||||
"pezpallet-collective/try-runtime",
|
|
||||||
"pezpallet-contracts/try-runtime",
|
|
||||||
"pezpallet-default-config-example/try-runtime",
|
|
||||||
"pezpallet-democracy/try-runtime",
|
|
||||||
"pezpallet-example-authorization-tx-extension/try-runtime",
|
|
||||||
"pezpallet-example-offchain-worker/try-runtime",
|
|
||||||
"pezpallet-example-single-block-migrations/try-runtime",
|
|
||||||
"pezpallet-examples/try-runtime",
|
|
||||||
"pezpallet-grandpa/try-runtime",
|
|
||||||
"pezpallet-nfts/try-runtime",
|
|
||||||
"pezpallet-preimage/try-runtime",
|
|
||||||
"pezpallet-scheduler/try-runtime",
|
|
||||||
"pezpallet-skip-feeless-payment/try-runtime",
|
|
||||||
"pezpallet-timestamp/try-runtime",
|
|
||||||
"pezpallet-transaction-payment/try-runtime",
|
|
||||||
"pezpallet-uniques/try-runtime",
|
|
||||||
"pezpallet-xcm/try-runtime",
|
|
||||||
"pezsc-chain-spec/try-runtime",
|
|
||||||
"pezsc-cli/try-runtime",
|
|
||||||
"pezsc-client-db/try-runtime",
|
|
||||||
"pezsc-consensus-aura/try-runtime",
|
|
||||||
"pezsc-consensus-babe/try-runtime",
|
|
||||||
"pezsc-consensus-beefy/try-runtime",
|
|
||||||
"pezsc-consensus-grandpa/try-runtime",
|
|
||||||
"pezsc-consensus-manual-seal/try-runtime",
|
|
||||||
"pezsc-consensus-pow/try-runtime",
|
|
||||||
"pezsc-executor/try-runtime",
|
|
||||||
"pezsc-network/try-runtime",
|
|
||||||
"pezsc-rpc-api/try-runtime",
|
|
||||||
"pezsc-rpc/try-runtime",
|
|
||||||
"pezsc-service/try-runtime",
|
|
||||||
"pezsp-api/try-runtime",
|
|
||||||
"pezsp-genesis-builder/try-runtime",
|
|
||||||
"pezsp-keyring/try-runtime",
|
|
||||||
"pezsp-offchain/try-runtime",
|
|
||||||
"pezsp-runtime/try-runtime",
|
|
||||||
"pezsp-version/try-runtime",
|
|
||||||
"teyrchain-info/try-runtime",
|
|
||||||
"xcm-builder/try-runtime",
|
|
||||||
"xcm-executor/try-runtime",
|
|
||||||
"xcm-pez-docs/try-runtime",
|
|
||||||
"xcm-pez-simulator/try-runtime",
|
|
||||||
"xcm/try-runtime",
|
|
||||||
]
|
|
||||||
serde = []
|
|
||||||
experimental = []
|
|
||||||
with-tracing = []
|
|
||||||
tuples-96 = []
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<script> mermaid.init({ startOnLoad: true, theme: "dark" }, "pre.language-mermaid > code");</script>
|
|
||||||
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
<script>
|
|
||||||
function createToC() {
|
|
||||||
let sidebar = document.querySelector(".sidebar");
|
|
||||||
let headers = document.querySelectorAll("#main-content h2, #main-content h3, #main-content h4");
|
|
||||||
console.log(`detected polkadot_sdk_docs: headers: ${headers.length}`);
|
|
||||||
|
|
||||||
let toc = document.createElement("div");
|
|
||||||
toc.classList.add("sidebar-table-of-contents");
|
|
||||||
toc.appendChild(document.createElement("h2").appendChild(document.createTextNode("Table of Contents")).parentNode);
|
|
||||||
|
|
||||||
let modules = document.querySelectorAll("main .item-table a.mod");
|
|
||||||
|
|
||||||
// the first two headers are always junk
|
|
||||||
headers.forEach(header => {
|
|
||||||
let link = document.createElement("a");
|
|
||||||
link.href = "#" + header.id;
|
|
||||||
const headerTextContent = header.textContent.replace("§", "")
|
|
||||||
link.textContent = headerTextContent;
|
|
||||||
link.className = header.tagName.toLowerCase();
|
|
||||||
|
|
||||||
toc.appendChild(link);
|
|
||||||
|
|
||||||
if (header.id == "modules" && headerTextContent == "Modules") {
|
|
||||||
modules.forEach(module => {
|
|
||||||
let link = document.createElement("a");
|
|
||||||
link.href = module.href;
|
|
||||||
link.textContent = module.textContent;
|
|
||||||
link.className = "h3";
|
|
||||||
|
|
||||||
toc.appendChild(link);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// insert toc as the second child in sidebar
|
|
||||||
let sidebar_children = sidebar.children;
|
|
||||||
if (sidebar_children.length > 1) {
|
|
||||||
sidebar.insertBefore(toc, sidebar_children[1]);
|
|
||||||
} else {
|
|
||||||
sidebar.appendChild(toc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideSidebarElements() {
|
|
||||||
// Create the 'Expand for More' button
|
|
||||||
var expandButton = document.createElement('button');
|
|
||||||
expandButton.innerText = 'Expand More Items';
|
|
||||||
expandButton.classList.add('expand-button');
|
|
||||||
|
|
||||||
// Insert the button at the top of the sidebar or before the '.sidebar-elems'
|
|
||||||
var sidebarElems = document.querySelector('.sidebar-elems');
|
|
||||||
sidebarElems.parentNode.insertBefore(expandButton, sidebarElems);
|
|
||||||
|
|
||||||
// Initially hide the '.sidebar-elems'
|
|
||||||
sidebarElems.style.display = 'none';
|
|
||||||
|
|
||||||
// Add click event listener to the button
|
|
||||||
expandButton.addEventListener('click', function () {
|
|
||||||
// Toggle the display of the '.sidebar-elems'
|
|
||||||
if (sidebarElems.style.display === 'none') {
|
|
||||||
sidebarElems.style.display = 'block';
|
|
||||||
expandButton.innerText = 'Collapse';
|
|
||||||
} else {
|
|
||||||
sidebarElems.style.display = 'none';
|
|
||||||
expandButton.innerText = 'Expand for More';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("DOMContentLoaded", (event) => {
|
|
||||||
// if the crate is one that starts with `polkadot_sdk_docs`
|
|
||||||
let crate_name = document.querySelector("#main-content > div > h1 > a:nth-child(1)");
|
|
||||||
if (!crate_name.textContent.startsWith("polkadot_sdk_docs")) {
|
|
||||||
console.log("skipping -- not `polkadot_sdk_docs`");
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
// insert class 'sdk-docs' to the body, so it enables the custom css rules.
|
|
||||||
document.body.classList.add("sdk-docs");
|
|
||||||
}
|
|
||||||
|
|
||||||
createToC();
|
|
||||||
hideSidebarElements();
|
|
||||||
|
|
||||||
console.log("updating page based on being `polkadot_sdk_docs` crate");
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
body.sdk-docs {
|
|
||||||
nav.side-bar {
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-table-of-contents {
|
|
||||||
margin-bottom: 1em;
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-table-of-contents a {
|
|
||||||
display: block;
|
|
||||||
margin: 0.2em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-table-of-contents .h2 {
|
|
||||||
font-weight: bold;
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-table-of-contents .h3 {
|
|
||||||
margin-left: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-table-of-contents .h4 {
|
|
||||||
margin-left: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar h2.location {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-elems {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Center the 'Expand for More' button */
|
|
||||||
.expand-button {
|
|
||||||
display: inline-block;
|
|
||||||
/* Use inline-block for sizing */
|
|
||||||
margin: 10px auto;
|
|
||||||
/* Auto margins for horizontal centering */
|
|
||||||
padding: 5px 10px;
|
|
||||||
background-color: #007bff;
|
|
||||||
color: white;
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
border: none;
|
|
||||||
border-radius: 5px;
|
|
||||||
width: auto;
|
|
||||||
/* Centering the button within its parent container */
|
|
||||||
position: relative;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
:root {
|
|
||||||
--polkadot-pink: #E6007A;
|
|
||||||
--polkadot-green: #56F39A;
|
|
||||||
--polkadot-lime: #D3FF33;
|
|
||||||
--polkadot-cyan: #00B2FF;
|
|
||||||
--polkadot-purple: #552BBF;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Light theme */
|
|
||||||
html[data-theme="light"] {
|
|
||||||
--quote-background: #f9f9f9;
|
|
||||||
--quote-border: #ccc;
|
|
||||||
--quote-text: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dark theme */
|
|
||||||
html[data-theme="dark"] {
|
|
||||||
--quote-background: #333;
|
|
||||||
--quote-border: #555;
|
|
||||||
--quote-text: #f9f9f9;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ayu theme */
|
|
||||||
html[data-theme="ayu"] {
|
|
||||||
--quote-background: #272822;
|
|
||||||
--quote-border: #383830;
|
|
||||||
--quote-text: #f8f8f2;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.sdk-docs {
|
|
||||||
nav.sidebar>div.sidebar-crate>a>img {
|
|
||||||
width: 190px;
|
|
||||||
height: 52px;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav.sidebar {
|
|
||||||
flex: 0 0 250px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
html[data-theme="light"] .sidebar-crate > .logo-container > img {
|
|
||||||
content: url("https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/docs/images/Polkadot_Logo_Horizontal_Pink_Black.png");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Custom styles for blockquotes */
|
|
||||||
blockquote {
|
|
||||||
background-color: var(--quote-background);
|
|
||||||
border-left: 5px solid var(--quote-border);
|
|
||||||
color: var(--quote-text);
|
|
||||||
margin: 1em 0;
|
|
||||||
padding: 1em 1.5em;
|
|
||||||
/* font-style: italic; */
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "pezkuwi-sdk-docs-first-pezpallet"
|
|
||||||
description = "A simple pezpallet created for the pezkuwi-sdk-docs guides"
|
|
||||||
version = "0.0.0"
|
|
||||||
license = "MIT-0"
|
|
||||||
authors.workspace = true
|
|
||||||
homepage.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
publish = false
|
|
||||||
documentation.workspace = true
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
codec = { workspace = true }
|
|
||||||
docify = { workspace = true }
|
|
||||||
pezframe = { workspace = true, features = ["runtime"] }
|
|
||||||
pezframe-support = { workspace = true }
|
|
||||||
pezframe-system = { workspace = true }
|
|
||||||
scale-info = { workspace = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["std"]
|
|
||||||
std = [
|
|
||||||
"codec/std",
|
|
||||||
"pezframe-support/std",
|
|
||||||
"pezframe-system/std",
|
|
||||||
"pezframe/std",
|
|
||||||
"scale-info/std",
|
|
||||||
]
|
|
||||||
runtime-benchmarks = [
|
|
||||||
"pezframe-support/runtime-benchmarks",
|
|
||||||
"pezframe-system/runtime-benchmarks",
|
|
||||||
"pezframe/runtime-benchmarks",
|
|
||||||
]
|
|
||||||
try-runtime = [
|
|
||||||
"pezframe-support/try-runtime",
|
|
||||||
"pezframe-system/try-runtime",
|
|
||||||
"pezframe/try-runtime",
|
|
||||||
]
|
|
||||||
serde = []
|
|
||||||
experimental = []
|
|
||||||
tuples-96 = []
|
|
||||||
@@ -1,485 +0,0 @@
|
|||||||
// This file is part of Bizinikiwi.
|
|
||||||
|
|
||||||
// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
//! Pallets used in the `your_first_pallet` guide.
|
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[pezframe::pezpallet(dev_mode)]
|
|
||||||
pub mod shell_pallet {
|
|
||||||
use pezframe::prelude::*;
|
|
||||||
|
|
||||||
#[pezpallet::config]
|
|
||||||
pub trait Config: pezframe_system::Config {}
|
|
||||||
|
|
||||||
#[pezpallet::pezpallet]
|
|
||||||
pub struct Pezpallet<T>(_);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pezframe::pezpallet(dev_mode)]
|
|
||||||
pub mod pezpallet {
|
|
||||||
use pezframe::prelude::*;
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
pub type Balance = u128;
|
|
||||||
|
|
||||||
#[pezpallet::config]
|
|
||||||
pub trait Config: pezframe_system::Config {}
|
|
||||||
|
|
||||||
#[pezpallet::pezpallet]
|
|
||||||
pub struct Pezpallet<T>(_);
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
/// Single storage item, of type `Balance`.
|
|
||||||
#[pezpallet::storage]
|
|
||||||
pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
/// A mapping from `T::AccountId` to `Balance`
|
|
||||||
#[pezpallet::storage]
|
|
||||||
pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
|
|
||||||
|
|
||||||
#[docify::export(impl_pallet)]
|
|
||||||
#[pezpallet::call]
|
|
||||||
impl<T: Config> Pezpallet<T> {
|
|
||||||
/// An unsafe mint that can be called by anyone. Not a great idea.
|
|
||||||
pub fn mint_unsafe(
|
|
||||||
origin: T::RuntimeOrigin,
|
|
||||||
dest: T::AccountId,
|
|
||||||
amount: Balance,
|
|
||||||
) -> DispatchResult {
|
|
||||||
// ensure that this is a signed account, but we don't really check `_anyone`.
|
|
||||||
let _anyone = ensure_signed(origin)?;
|
|
||||||
|
|
||||||
// update the balances map. Notice how all `<T: Config>` remains as `<T>`.
|
|
||||||
Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
|
|
||||||
// update total issuance.
|
|
||||||
TotalIssuance::<T>::mutate(|t| *t = Some(t.unwrap_or(0) + amount));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transfer `amount` from `origin` to `dest`.
|
|
||||||
pub fn transfer(
|
|
||||||
origin: T::RuntimeOrigin,
|
|
||||||
dest: T::AccountId,
|
|
||||||
amount: Balance,
|
|
||||||
) -> DispatchResult {
|
|
||||||
let sender = ensure_signed(origin)?;
|
|
||||||
|
|
||||||
// ensure sender has enough balance, and if so, calculate what is left after `amount`.
|
|
||||||
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
|
|
||||||
if sender_balance < amount {
|
|
||||||
return Err("InsufficientBalance".into());
|
|
||||||
}
|
|
||||||
let remainder = sender_balance - amount;
|
|
||||||
|
|
||||||
// update sender and dest balances.
|
|
||||||
Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
|
|
||||||
Balances::<T>::insert(&sender, remainder);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
impl<T: Config> Pezpallet<T> {
|
|
||||||
#[docify::export]
|
|
||||||
pub fn transfer_better(
|
|
||||||
origin: T::RuntimeOrigin,
|
|
||||||
dest: T::AccountId,
|
|
||||||
amount: Balance,
|
|
||||||
) -> DispatchResult {
|
|
||||||
let sender = ensure_signed(origin)?;
|
|
||||||
|
|
||||||
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
|
|
||||||
ensure!(sender_balance >= amount, "InsufficientBalance");
|
|
||||||
let remainder = sender_balance - amount;
|
|
||||||
|
|
||||||
// .. snip
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
/// Transfer `amount` from `origin` to `dest`.
|
|
||||||
pub fn transfer_better_checked(
|
|
||||||
origin: T::RuntimeOrigin,
|
|
||||||
dest: T::AccountId,
|
|
||||||
amount: Balance,
|
|
||||||
) -> DispatchResult {
|
|
||||||
let sender = ensure_signed(origin)?;
|
|
||||||
|
|
||||||
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
|
|
||||||
let remainder = sender_balance.checked_sub(amount).ok_or("InsufficientBalance")?;
|
|
||||||
|
|
||||||
// .. snip
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(test, doc))]
|
|
||||||
pub(crate) mod tests {
|
|
||||||
use crate::pezpallet::*;
|
|
||||||
|
|
||||||
#[docify::export(testing_prelude)]
|
|
||||||
use pezframe::testing_prelude::*;
|
|
||||||
|
|
||||||
pub(crate) const ALICE: u64 = 1;
|
|
||||||
pub(crate) const BOB: u64 = 2;
|
|
||||||
pub(crate) const CHARLIE: u64 = 3;
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
// This runtime is only used for testing, so it should be somewhere like `#[cfg(test)] mod
|
|
||||||
// tests { .. }`
|
|
||||||
mod runtime {
|
|
||||||
use super::*;
|
|
||||||
// we need to reference our `mod pezpallet` as an identifier to pass to
|
|
||||||
// `construct_runtime`.
|
|
||||||
// YOU HAVE TO CHANGE THIS LINE BASED ON YOUR TEMPLATE
|
|
||||||
use crate::pezpallet as pezpallet_currency;
|
|
||||||
|
|
||||||
construct_runtime!(
|
|
||||||
pub enum Runtime {
|
|
||||||
// ---^^^^^^ This is where `enum Runtime` is defined.
|
|
||||||
System: pezframe_system,
|
|
||||||
Currency: pezpallet_currency,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
|
|
||||||
impl pezframe_system::Config for Runtime {
|
|
||||||
type Block = MockBlock<Runtime>;
|
|
||||||
// within pezpallet we just said `<T as pezframe_system::Config>::AccountId`, now we
|
|
||||||
// finally specified it.
|
|
||||||
type AccountId = u64;
|
|
||||||
}
|
|
||||||
|
|
||||||
// our simple pezpallet has nothing to be configured.
|
|
||||||
impl pezpallet_currency::Config for Runtime {}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) use runtime::*;
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
#[docify::export]
|
|
||||||
fn new_test_state_basic() -> TestState {
|
|
||||||
let mut state = TestState::new_empty();
|
|
||||||
let accounts = vec![(ALICE, 100), (BOB, 100)];
|
|
||||||
state.execute_with(|| {
|
|
||||||
for (who, amount) in &accounts {
|
|
||||||
Balances::<Runtime>::insert(who, amount);
|
|
||||||
TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
state
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
pub(crate) struct StateBuilder {
|
|
||||||
balances: Vec<(<Runtime as pezframe_system::Config>::AccountId, Balance)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export(default_state_builder)]
|
|
||||||
impl Default for StateBuilder {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self { balances: vec![(ALICE, 100), (BOB, 100)] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export(impl_state_builder_add)]
|
|
||||||
impl StateBuilder {
|
|
||||||
fn add_balance(
|
|
||||||
mut self,
|
|
||||||
who: <Runtime as pezframe_system::Config>::AccountId,
|
|
||||||
amount: Balance,
|
|
||||||
) -> Self {
|
|
||||||
self.balances.push((who, amount));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export(impl_state_builder_build)]
|
|
||||||
impl StateBuilder {
|
|
||||||
pub(crate) fn build_and_execute(self, test: impl FnOnce() -> ()) {
|
|
||||||
let mut ext = TestState::new_empty();
|
|
||||||
ext.execute_with(|| {
|
|
||||||
for (who, amount) in &self.balances {
|
|
||||||
Balances::<Runtime>::insert(who, amount);
|
|
||||||
TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ext.execute_with(test);
|
|
||||||
|
|
||||||
// assertions that must always hold
|
|
||||||
ext.execute_with(|| {
|
|
||||||
assert_eq!(
|
|
||||||
Balances::<Runtime>::iter().map(|(_, x)| x).sum::<u128>(),
|
|
||||||
TotalIssuance::<Runtime>::get().unwrap_or_default()
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[test]
|
|
||||||
fn first_test() {
|
|
||||||
TestState::new_empty().execute_with(|| {
|
|
||||||
// We expect Alice's account to have no funds.
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&ALICE), None);
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), None);
|
|
||||||
|
|
||||||
// mint some funds into Alice's account.
|
|
||||||
assert_ok!(Pezpallet::<Runtime>::mint_unsafe(
|
|
||||||
RuntimeOrigin::signed(ALICE),
|
|
||||||
ALICE,
|
|
||||||
100
|
|
||||||
));
|
|
||||||
|
|
||||||
// re-check the above
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(100));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[test]
|
|
||||||
fn state_builder_works() {
|
|
||||||
StateBuilder::default().build_and_execute(|| {
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[test]
|
|
||||||
fn state_builder_add_balance() {
|
|
||||||
StateBuilder::default().add_balance(CHARLIE, 42).build_and_execute(|| {
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(42));
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn state_builder_duplicate_genesis_fails() {
|
|
||||||
StateBuilder::default()
|
|
||||||
.add_balance(CHARLIE, 42)
|
|
||||||
.add_balance(CHARLIE, 43)
|
|
||||||
.build_and_execute(|| {
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[test]
|
|
||||||
fn mint_works() {
|
|
||||||
StateBuilder::default().build_and_execute(|| {
|
|
||||||
// given the initial state, when:
|
|
||||||
assert_ok!(Pezpallet::<Runtime>::mint_unsafe(
|
|
||||||
RuntimeOrigin::signed(ALICE),
|
|
||||||
BOB,
|
|
||||||
100
|
|
||||||
));
|
|
||||||
|
|
||||||
// then:
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(200));
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(300));
|
|
||||||
|
|
||||||
// given:
|
|
||||||
assert_ok!(Pezpallet::<Runtime>::mint_unsafe(
|
|
||||||
RuntimeOrigin::signed(ALICE),
|
|
||||||
CHARLIE,
|
|
||||||
100
|
|
||||||
));
|
|
||||||
|
|
||||||
// then:
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(100));
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(400));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[test]
|
|
||||||
fn transfer_works() {
|
|
||||||
StateBuilder::default().build_and_execute(|| {
|
|
||||||
// given the initial state, when:
|
|
||||||
assert_ok!(Pezpallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
|
|
||||||
|
|
||||||
// then:
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
|
||||||
|
|
||||||
// when:
|
|
||||||
assert_ok!(Pezpallet::<Runtime>::transfer(RuntimeOrigin::signed(BOB), ALICE, 50));
|
|
||||||
|
|
||||||
// then:
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[test]
|
|
||||||
fn transfer_from_non_existent_fails() {
|
|
||||||
StateBuilder::default().build_and_execute(|| {
|
|
||||||
// given the initial state, when:
|
|
||||||
assert_err!(
|
|
||||||
Pezpallet::<Runtime>::transfer(RuntimeOrigin::signed(CHARLIE), ALICE, 10),
|
|
||||||
"NonExistentAccount"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then nothing has changed.
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pezframe::pezpallet(dev_mode)]
|
|
||||||
pub mod pezpallet_v2 {
|
|
||||||
use super::pezpallet::Balance;
|
|
||||||
use pezframe::prelude::*;
|
|
||||||
|
|
||||||
#[docify::export(config_v2)]
|
|
||||||
#[pezpallet::config]
|
|
||||||
pub trait Config: pezframe_system::Config {
|
|
||||||
/// The overarching event type of the runtime.
|
|
||||||
#[allow(deprecated)]
|
|
||||||
type RuntimeEvent: From<Event<Self>>
|
|
||||||
+ IsType<<Self as pezframe_system::Config>::RuntimeEvent>
|
|
||||||
+ TryInto<Event<Self>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pezpallet::pezpallet]
|
|
||||||
pub struct Pezpallet<T>(_);
|
|
||||||
|
|
||||||
#[pezpallet::storage]
|
|
||||||
pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
|
|
||||||
|
|
||||||
#[pezpallet::storage]
|
|
||||||
pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[pezpallet::error]
|
|
||||||
pub enum Error<T> {
|
|
||||||
/// Account does not exist.
|
|
||||||
NonExistentAccount,
|
|
||||||
/// Account does not have enough balance.
|
|
||||||
InsufficientBalance,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[pezpallet::event]
|
|
||||||
#[pezpallet::generate_deposit(pub(super) fn deposit_event)]
|
|
||||||
pub enum Event<T: Config> {
|
|
||||||
/// A transfer succeeded.
|
|
||||||
Transferred { from: T::AccountId, to: T::AccountId, amount: Balance },
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pezpallet::call]
|
|
||||||
impl<T: Config> Pezpallet<T> {
|
|
||||||
#[docify::export(transfer_v2)]
|
|
||||||
pub fn transfer(
|
|
||||||
origin: T::RuntimeOrigin,
|
|
||||||
dest: T::AccountId,
|
|
||||||
amount: Balance,
|
|
||||||
) -> DispatchResult {
|
|
||||||
let sender = ensure_signed(origin)?;
|
|
||||||
|
|
||||||
// ensure sender has enough balance, and if so, calculate what is left after `amount`.
|
|
||||||
let sender_balance =
|
|
||||||
Balances::<T>::get(&sender).ok_or(Error::<T>::NonExistentAccount)?;
|
|
||||||
let remainder =
|
|
||||||
sender_balance.checked_sub(amount).ok_or(Error::<T>::InsufficientBalance)?;
|
|
||||||
|
|
||||||
Balances::<T>::mutate(&dest, |b| *b = Some(b.unwrap_or(0) + amount));
|
|
||||||
Balances::<T>::insert(&sender, remainder);
|
|
||||||
|
|
||||||
Self::deposit_event(Event::<T>::Transferred { from: sender, to: dest, amount });
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(test, doc))]
|
|
||||||
pub mod tests {
|
|
||||||
use super::{super::pezpallet::tests::StateBuilder, *};
|
|
||||||
use pezframe::testing_prelude::*;
|
|
||||||
const ALICE: u64 = 1;
|
|
||||||
const BOB: u64 = 2;
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
pub mod runtime_v2 {
|
|
||||||
use super::*;
|
|
||||||
use crate::pezpallet_v2 as pezpallet_currency;
|
|
||||||
|
|
||||||
construct_runtime!(
|
|
||||||
pub enum Runtime {
|
|
||||||
System: pezframe_system,
|
|
||||||
Currency: pezpallet_currency,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
|
|
||||||
impl pezframe_system::Config for Runtime {
|
|
||||||
type Block = MockBlock<Runtime>;
|
|
||||||
type AccountId = u64;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pezpallet_currency::Config for Runtime {
|
|
||||||
type RuntimeEvent = RuntimeEvent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) use runtime_v2::*;
|
|
||||||
|
|
||||||
#[docify::export(transfer_works_v2)]
|
|
||||||
#[test]
|
|
||||||
fn transfer_works() {
|
|
||||||
StateBuilder::default().build_and_execute(|| {
|
|
||||||
// skip the genesis block, as events are not deposited there and we need them for
|
|
||||||
// the final assertion.
|
|
||||||
System::set_block_number(ALICE);
|
|
||||||
|
|
||||||
// given the initial state, when:
|
|
||||||
assert_ok!(Pezpallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
|
|
||||||
|
|
||||||
// then:
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
|
||||||
|
|
||||||
// now we can also check that an event has been deposited:
|
|
||||||
assert_eq!(
|
|
||||||
System::read_events_for_pallet::<Event<Runtime>>(),
|
|
||||||
vec![Event::Transferred { from: ALICE, to: BOB, amount: 50 }]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "pezkuwi-sdk-docs-first-runtime"
|
|
||||||
description = "A simple runtime created for the pezkuwi-sdk-docs guides"
|
|
||||||
version = "0.0.0"
|
|
||||||
license = "MIT-0"
|
|
||||||
authors.workspace = true
|
|
||||||
homepage.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
publish = false
|
|
||||||
documentation.workspace = true
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
codec = { workspace = true }
|
|
||||||
scale-info = { workspace = true }
|
|
||||||
serde_json = { workspace = true }
|
|
||||||
|
|
||||||
# this is a frame-based runtime, thus importing `frame` with runtime feature enabled.
|
|
||||||
pezframe = { workspace = true, features = ["runtime"] }
|
|
||||||
pezframe-support = { workspace = true }
|
|
||||||
pezframe-system = { workspace = true }
|
|
||||||
|
|
||||||
# pallets that we want to use
|
|
||||||
pezpallet-balances = { workspace = true }
|
|
||||||
pezpallet-sudo = { workspace = true }
|
|
||||||
pezpallet-timestamp = { workspace = true }
|
|
||||||
pezpallet-transaction-payment = { workspace = true }
|
|
||||||
pezpallet-transaction-payment-rpc-runtime-api = { workspace = true }
|
|
||||||
|
|
||||||
# other pezkuwi-sdk-deps
|
|
||||||
pezframe-system-rpc-runtime-api = { workspace = true }
|
|
||||||
pezsp-api = { workspace = true }
|
|
||||||
pezsp-block-builder = { workspace = true }
|
|
||||||
pezsp-core = { workspace = true }
|
|
||||||
pezsp-genesis-builder = { workspace = true }
|
|
||||||
pezsp-keyring = { workspace = true }
|
|
||||||
pezsp-offchain = { workspace = true }
|
|
||||||
pezsp-runtime = { workspace = true }
|
|
||||||
pezsp-session = { workspace = true }
|
|
||||||
pezsp-transaction-pool = { workspace = true }
|
|
||||||
|
|
||||||
# local pezpallet templates
|
|
||||||
first-pezpallet = { workspace = true }
|
|
||||||
|
|
||||||
docify = { workspace = true }
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
bizinikiwi-wasm-builder = { workspace = true, optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["std"]
|
|
||||||
std = [
|
|
||||||
"bizinikiwi-wasm-builder",
|
|
||||||
"bizinikiwi-wasm-builder?/std",
|
|
||||||
"codec/std",
|
|
||||||
"first-pezpallet/std",
|
|
||||||
"pezframe-support/std",
|
|
||||||
"pezframe-system-rpc-runtime-api/std",
|
|
||||||
"pezframe-system/std",
|
|
||||||
"pezframe/std",
|
|
||||||
"pezpallet-balances/std",
|
|
||||||
"pezpallet-sudo/std",
|
|
||||||
"pezpallet-timestamp/std",
|
|
||||||
"pezpallet-transaction-payment-rpc-runtime-api/std",
|
|
||||||
"pezpallet-transaction-payment/std",
|
|
||||||
"pezsp-api/std",
|
|
||||||
"pezsp-block-builder/std",
|
|
||||||
"pezsp-core/std",
|
|
||||||
"pezsp-genesis-builder/std",
|
|
||||||
"pezsp-keyring/std",
|
|
||||||
"pezsp-offchain/std",
|
|
||||||
"pezsp-runtime/std",
|
|
||||||
"pezsp-session/std",
|
|
||||||
"pezsp-transaction-pool/std",
|
|
||||||
"scale-info/std",
|
|
||||||
"serde_json/std",
|
|
||||||
]
|
|
||||||
runtime-benchmarks = [
|
|
||||||
"bizinikiwi-wasm-builder?/runtime-benchmarks",
|
|
||||||
"first-pezpallet/runtime-benchmarks",
|
|
||||||
"pezframe-support/runtime-benchmarks",
|
|
||||||
"pezframe-system-rpc-runtime-api/runtime-benchmarks",
|
|
||||||
"pezframe-system/runtime-benchmarks",
|
|
||||||
"pezframe/runtime-benchmarks",
|
|
||||||
"pezpallet-balances/runtime-benchmarks",
|
|
||||||
"pezpallet-sudo/runtime-benchmarks",
|
|
||||||
"pezpallet-timestamp/runtime-benchmarks",
|
|
||||||
"pezpallet-transaction-payment-rpc-runtime-api/runtime-benchmarks",
|
|
||||||
"pezpallet-transaction-payment/runtime-benchmarks",
|
|
||||||
"pezsp-api/runtime-benchmarks",
|
|
||||||
"pezsp-block-builder/runtime-benchmarks",
|
|
||||||
"pezsp-genesis-builder/runtime-benchmarks",
|
|
||||||
"pezsp-keyring/runtime-benchmarks",
|
|
||||||
"pezsp-offchain/runtime-benchmarks",
|
|
||||||
"pezsp-runtime/runtime-benchmarks",
|
|
||||||
"pezsp-session/runtime-benchmarks",
|
|
||||||
"pezsp-transaction-pool/runtime-benchmarks",
|
|
||||||
]
|
|
||||||
try-runtime = [
|
|
||||||
"first-pezpallet/try-runtime",
|
|
||||||
"pezframe-support/try-runtime",
|
|
||||||
"pezframe-system/try-runtime",
|
|
||||||
"pezframe/try-runtime",
|
|
||||||
"pezpallet-balances/try-runtime",
|
|
||||||
"pezpallet-sudo/try-runtime",
|
|
||||||
"pezpallet-timestamp/try-runtime",
|
|
||||||
"pezpallet-transaction-payment-rpc-runtime-api/try-runtime",
|
|
||||||
"pezpallet-transaction-payment/try-runtime",
|
|
||||||
"pezsp-api/try-runtime",
|
|
||||||
"pezsp-block-builder/try-runtime",
|
|
||||||
"pezsp-genesis-builder/try-runtime",
|
|
||||||
"pezsp-keyring/try-runtime",
|
|
||||||
"pezsp-offchain/try-runtime",
|
|
||||||
"pezsp-runtime/try-runtime",
|
|
||||||
"pezsp-session/try-runtime",
|
|
||||||
"pezsp-transaction-pool/try-runtime",
|
|
||||||
]
|
|
||||||
serde = []
|
|
||||||
experimental = []
|
|
||||||
tuples-96 = []
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
// This file is part of Bizinikiwi.
|
|
||||||
|
|
||||||
// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
{
|
|
||||||
bizinikiwi_wasm_builder::WasmBuilder::new()
|
|
||||||
.with_current_project()
|
|
||||||
.export_heap_base()
|
|
||||||
.import_memory()
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,302 +0,0 @@
|
|||||||
// This file is part of Bizinikiwi.
|
|
||||||
|
|
||||||
// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
//! Runtime used in `your_first_runtime`.
|
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
use alloc::{vec, vec::Vec};
|
|
||||||
use first_pezpallet::pezpallet_v2 as our_first_pallet;
|
|
||||||
use pezframe::{deps::pezsp_genesis_builder::DEV_RUNTIME_PRESET, prelude::*, runtime::prelude::*};
|
|
||||||
use pezpallet_transaction_payment_rpc_runtime_api::{FeeDetails, RuntimeDispatchInfo};
|
|
||||||
use pezsp_keyring::Sr25519Keyring;
|
|
||||||
use pezsp_runtime::{
|
|
||||||
traits::Block as BlockT,
|
|
||||||
transaction_validity::{TransactionSource, TransactionValidity},
|
|
||||||
ApplyExtrinsicResult,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[runtime_version]
|
|
||||||
pub const VERSION: RuntimeVersion = RuntimeVersion {
|
|
||||||
spec_name: alloc::borrow::Cow::Borrowed("first-runtime"),
|
|
||||||
impl_name: alloc::borrow::Cow::Borrowed("first-runtime"),
|
|
||||||
authoring_version: 1,
|
|
||||||
spec_version: 0,
|
|
||||||
impl_version: 1,
|
|
||||||
apis: RUNTIME_API_VERSIONS,
|
|
||||||
transaction_version: 1,
|
|
||||||
system_version: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[docify::export(cr)]
|
|
||||||
construct_runtime!(
|
|
||||||
pub struct Runtime {
|
|
||||||
// Mandatory for all runtimes
|
|
||||||
System: pezframe_system,
|
|
||||||
|
|
||||||
// A number of other pallets from FRAME.
|
|
||||||
Timestamp: pezpallet_timestamp,
|
|
||||||
Balances: pezpallet_balances,
|
|
||||||
Sudo: pezpallet_sudo,
|
|
||||||
TransactionPayment: pezpallet_transaction_payment,
|
|
||||||
|
|
||||||
// Our local pezpallet
|
|
||||||
FirstPallet: our_first_pallet,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[docify::export_content]
|
|
||||||
mod runtime_types {
|
|
||||||
use super::*;
|
|
||||||
pub(super) type SignedExtra = (
|
|
||||||
// `frame` already provides all the signed extensions from `pezframe-system`. We just add
|
|
||||||
// the one related to tx-payment here.
|
|
||||||
pezframe::runtime::types_common::SystemTransactionExtensionsOf<Runtime>,
|
|
||||||
pezpallet_transaction_payment::ChargeTransactionPayment<Runtime>,
|
|
||||||
);
|
|
||||||
|
|
||||||
pub(super) type Block = pezframe::runtime::types_common::BlockOf<Runtime, SignedExtra>;
|
|
||||||
pub(super) type _Header = HeaderFor<Runtime>;
|
|
||||||
|
|
||||||
pub(super) type RuntimeExecutive = Executive<
|
|
||||||
Runtime,
|
|
||||||
Block,
|
|
||||||
pezframe_system::ChainContext<Runtime>,
|
|
||||||
Runtime,
|
|
||||||
AllPalletsWithSystem,
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
use runtime_types::*;
|
|
||||||
|
|
||||||
#[docify::export_content]
|
|
||||||
mod config_impls {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
parameter_types! {
|
|
||||||
pub const Version: RuntimeVersion = VERSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive_impl(pezframe_system::config_preludes::SolochainDefaultConfig)]
|
|
||||||
impl pezframe_system::Config for Runtime {
|
|
||||||
type Block = Block;
|
|
||||||
type Version = Version;
|
|
||||||
type AccountData =
|
|
||||||
pezpallet_balances::AccountData<<Runtime as pezpallet_balances::Config>::Balance>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive_impl(pezpallet_balances::config_preludes::TestDefaultConfig)]
|
|
||||||
impl pezpallet_balances::Config for Runtime {
|
|
||||||
type AccountStore = System;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive_impl(pezpallet_sudo::config_preludes::TestDefaultConfig)]
|
|
||||||
impl pezpallet_sudo::Config for Runtime {}
|
|
||||||
|
|
||||||
#[derive_impl(pezpallet_timestamp::config_preludes::TestDefaultConfig)]
|
|
||||||
impl pezpallet_timestamp::Config for Runtime {}
|
|
||||||
|
|
||||||
#[derive_impl(pezpallet_transaction_payment::config_preludes::TestDefaultConfig)]
|
|
||||||
impl pezpallet_transaction_payment::Config for Runtime {
|
|
||||||
type OnChargeTransaction = pezpallet_transaction_payment::FungibleAdapter<Balances, ()>;
|
|
||||||
// We specify a fixed length to fee here, which essentially means all transactions charge
|
|
||||||
// exactly 1 unit of fee.
|
|
||||||
type LengthToFee = FixedFee<1, <Self as pezpallet_balances::Config>::Balance>;
|
|
||||||
type WeightToFee = NoFee<<Self as pezpallet_balances::Config>::Balance>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export(our_config_impl)]
|
|
||||||
impl our_first_pallet::Config for Runtime {
|
|
||||||
type RuntimeEvent = RuntimeEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Provides getters for genesis configuration presets.
|
|
||||||
pub mod genesis_config_presets {
|
|
||||||
use super::*;
|
|
||||||
use crate::{
|
|
||||||
interface::{Balance, MinimumBalance},
|
|
||||||
BalancesConfig, RuntimeGenesisConfig, SudoConfig,
|
|
||||||
};
|
|
||||||
use pezframe::deps::pezframe_support::build_struct_json_patch;
|
|
||||||
use serde_json::Value;
|
|
||||||
|
|
||||||
/// Returns a development genesis config preset.
|
|
||||||
#[docify::export]
|
|
||||||
pub fn development_config_genesis() -> Value {
|
|
||||||
let endowment = <MinimumBalance as Get<Balance>>::get().max(1) * 1000;
|
|
||||||
build_struct_json_patch!(RuntimeGenesisConfig {
|
|
||||||
balances: BalancesConfig {
|
|
||||||
balances: Sr25519Keyring::iter()
|
|
||||||
.map(|a| (a.to_account_id(), endowment))
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
},
|
|
||||||
sudo: SudoConfig { key: Some(Sr25519Keyring::Alice.to_account_id()) },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the set of the available genesis config presets.
|
|
||||||
#[docify::export]
|
|
||||||
pub fn get_preset(id: &PresetId) -> Option<Vec<u8>> {
|
|
||||||
let patch = match id.as_ref() {
|
|
||||||
DEV_RUNTIME_PRESET => development_config_genesis(),
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
Some(
|
|
||||||
serde_json::to_string(&patch)
|
|
||||||
.expect("serialization to json is expected to work. qed.")
|
|
||||||
.into_bytes(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// List of supported presets.
|
|
||||||
#[docify::export]
|
|
||||||
pub fn preset_names() -> Vec<PresetId> {
|
|
||||||
vec![PresetId::from(DEV_RUNTIME_PRESET)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_runtime_apis! {
|
|
||||||
impl pezsp_api::Core<Block> for Runtime {
|
|
||||||
fn version() -> RuntimeVersion {
|
|
||||||
VERSION
|
|
||||||
}
|
|
||||||
|
|
||||||
fn execute_block(block: <Block as BlockT>::LazyBlock) {
|
|
||||||
RuntimeExecutive::execute_block(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn initialize_block(header: &<Block as BlockT>::Header) -> ExtrinsicInclusionMode {
|
|
||||||
RuntimeExecutive::initialize_block(header)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pezsp_api::Metadata<Block> for Runtime {
|
|
||||||
fn metadata() -> OpaqueMetadata {
|
|
||||||
OpaqueMetadata::new(Runtime::metadata().into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn metadata_at_version(version: u32) -> Option<OpaqueMetadata> {
|
|
||||||
Runtime::metadata_at_version(version)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn metadata_versions() -> Vec<u32> {
|
|
||||||
Runtime::metadata_versions()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pezsp_block_builder::BlockBuilder<Block> for Runtime {
|
|
||||||
fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {
|
|
||||||
RuntimeExecutive::apply_extrinsic(extrinsic)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finalize_block() -> <Block as BlockT>::Header {
|
|
||||||
RuntimeExecutive::finalize_block()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inherent_extrinsics(data: InherentData) -> Vec<<Block as BlockT>::Extrinsic> {
|
|
||||||
data.create_extrinsics()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_inherents(
|
|
||||||
block: <Block as BlockT>::LazyBlock,
|
|
||||||
data: InherentData,
|
|
||||||
) -> CheckInherentsResult {
|
|
||||||
data.check_extrinsics(&block)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pezsp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {
|
|
||||||
fn validate_transaction(
|
|
||||||
source: TransactionSource,
|
|
||||||
tx: <Block as BlockT>::Extrinsic,
|
|
||||||
block_hash: <Block as BlockT>::Hash,
|
|
||||||
) -> TransactionValidity {
|
|
||||||
RuntimeExecutive::validate_transaction(source, tx, block_hash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pezsp_offchain::OffchainWorkerApi<Block> for Runtime {
|
|
||||||
fn offchain_worker(header: &<Block as BlockT>::Header) {
|
|
||||||
RuntimeExecutive::offchain_worker(header)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pezsp_session::SessionKeys<Block> for Runtime {
|
|
||||||
fn generate_session_keys(_seed: Option<Vec<u8>>) -> Vec<u8> {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decode_session_keys(
|
|
||||||
_encoded: Vec<u8>,
|
|
||||||
) -> Option<Vec<(Vec<u8>, pezsp_core::crypto::KeyTypeId)>> {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pezframe_system_rpc_runtime_api::AccountNonceApi<Block, interface::AccountId, interface::Nonce> for Runtime {
|
|
||||||
fn account_nonce(account: interface::AccountId) -> interface::Nonce {
|
|
||||||
System::account_nonce(account)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pezsp_genesis_builder::GenesisBuilder<Block> for Runtime {
|
|
||||||
fn build_state(config: Vec<u8>) -> GenesisBuilderResult {
|
|
||||||
build_state::<RuntimeGenesisConfig>(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_preset(id: &Option<PresetId>) -> Option<Vec<u8>> {
|
|
||||||
get_preset::<RuntimeGenesisConfig>(id, self::genesis_config_presets::get_preset)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn preset_names() -> Vec<PresetId> {
|
|
||||||
crate::genesis_config_presets::preset_names()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pezpallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<
|
|
||||||
Block,
|
|
||||||
interface::Balance,
|
|
||||||
> for Runtime {
|
|
||||||
fn query_info(uxt: <Block as BlockT>::Extrinsic, len: u32) -> RuntimeDispatchInfo<interface::Balance> {
|
|
||||||
TransactionPayment::query_info(uxt, len)
|
|
||||||
}
|
|
||||||
fn query_fee_details(uxt: <Block as BlockT>::Extrinsic, len: u32) -> FeeDetails<interface::Balance> {
|
|
||||||
TransactionPayment::query_fee_details(uxt, len)
|
|
||||||
}
|
|
||||||
fn query_weight_to_fee(weight: Weight) -> interface::Balance {
|
|
||||||
TransactionPayment::weight_to_fee(weight)
|
|
||||||
}
|
|
||||||
fn query_length_to_fee(length: u32) -> interface::Balance {
|
|
||||||
TransactionPayment::length_to_fee(length)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Just a handy re-definition of some types based on what is already provided to the pezpallet
|
|
||||||
/// configs.
|
|
||||||
pub mod interface {
|
|
||||||
use super::Runtime;
|
|
||||||
use pezframe::prelude::pezframe_system;
|
|
||||||
|
|
||||||
pub type AccountId = <Runtime as pezframe_system::Config>::AccountId;
|
|
||||||
pub type Nonce = <Runtime as pezframe_system::Config>::Nonce;
|
|
||||||
pub type Hash = <Runtime as pezframe_system::Config>::Hash;
|
|
||||||
pub type Balance = <Runtime as pezpallet_balances::Config>::Balance;
|
|
||||||
pub type MinimumBalance = <Runtime as pezpallet_balances::Config>::ExistentialDeposit;
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
//! # External Resources
|
|
||||||
//!
|
|
||||||
//! A non-exhaustive, un-opinionated list of external resources about Pezkuwi SDK.
|
|
||||||
//!
|
|
||||||
//! Unlike [`crate::guides`], or [`crate::pezkuwi_sdk::templates`] that contain material directly
|
|
||||||
//! maintained in the `pezkuwi-sdk` repository, the list of resources here are maintained by
|
|
||||||
//! third-parties, and are therefore subject to more variability. Any further resources may be added
|
|
||||||
//! by opening a pull request to the `pezkuwi-sdk` repository.
|
|
||||||
//!
|
|
||||||
//! - [Pezkuwi NFT Marketplace Tutorial by Pezkuwi Fellow Shawn Tabrizi](https://www.shawntabrizi.com/substrate-collectables-workshop/)
|
|
||||||
//! - [HEZ Code School](https://pezkuwichain.io/docs/introduction)
|
|
||||||
//! - [Pezkuwi Developers Github Organization](https://github.com/polkadot-developers/)
|
|
||||||
//! - [Pezkuwi Blockchain Academy](https://github.com/pezkuwichain/kurdistan_blockchain-akademy)
|
|
||||||
//! - [Pezkuwi Wiki](https://wiki.network.pezkuwichain.io/)
|
|
||||||
@@ -1,254 +0,0 @@
|
|||||||
//! # Upgrade Teyrchain for Asynchronous Backing Compatibility
|
|
||||||
//!
|
|
||||||
//! This guide is relevant for pezcumulus based teyrchain projects started in 2023 or before, whose
|
|
||||||
//! backing process is synchronous where parablocks can only be built on the latest Relay Chain
|
|
||||||
//! block. Async Backing allows collators to build parablocks on older Relay Chain blocks and create
|
|
||||||
//! pipelines of multiple pending parablocks. This parallel block generation increases efficiency
|
|
||||||
//! and throughput. For more information on Async backing and its terminology, refer to the document
|
|
||||||
//! on [the Pezkuwi SDK docs.](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/guides/async_backing_guide/index.html)
|
|
||||||
//!
|
|
||||||
//! > If starting a new teyrchain project, please use an async backing compatible template such as
|
|
||||||
//! > the
|
|
||||||
//! > [teyrchain template](https://github.com/pezkuwichain/pezkuwi-sdk/tree/main/templates/teyrchain).
|
|
||||||
//! The rollout process for Async Backing has three phases. Phases 1 and 2 below put new
|
|
||||||
//! infrastructure in place. Then we can simply turn on async backing in phase 3.
|
|
||||||
//!
|
|
||||||
//! ## Prerequisite
|
|
||||||
//!
|
|
||||||
//! The relay chain needs to have async backing enabled so double-check that the relay-chain
|
|
||||||
//! configuration contains the following three parameters (especially when testing locally e.g. with
|
|
||||||
//! zombienet):
|
|
||||||
//!
|
|
||||||
//! ```json
|
|
||||||
//! "async_backing_params": {
|
|
||||||
//! "max_candidate_depth": 3,
|
|
||||||
//! "allowed_ancestry_len": 2
|
|
||||||
//! },
|
|
||||||
//! "scheduling_lookahead": 2
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! <div class="warning"><code>scheduling_lookahead</code> must be set to 2, otherwise teyrchain
|
|
||||||
//! block times will degrade to worse than with sync backing!</div>
|
|
||||||
//!
|
|
||||||
//! ## Phase 1 - Update Teyrchain Runtime
|
|
||||||
//!
|
|
||||||
//! This phase involves configuring your teyrchain’s runtime `/runtime/src/lib.rs` to make use of
|
|
||||||
//! async backing system.
|
|
||||||
//!
|
|
||||||
//! 1. Establish and ensure constants for `capacity` and `velocity` are both set to 1 in the
|
|
||||||
//! runtime.
|
|
||||||
//! 2. Establish and ensure the constant relay chain slot duration measured in milliseconds equal to
|
|
||||||
//! `6000` in the runtime.
|
|
||||||
//! ```rust
|
|
||||||
//! // Maximum number of blocks simultaneously accepted by the Runtime, not yet included into the
|
|
||||||
//! // relay chain.
|
|
||||||
//! pub const UNINCLUDED_SEGMENT_CAPACITY: u32 = 1;
|
|
||||||
//! // How many teyrchain blocks are processed by the relay chain per parent. Limits the number of
|
|
||||||
//! // blocks authored per slot.
|
|
||||||
//! pub const BLOCK_PROCESSING_VELOCITY: u32 = 1;
|
|
||||||
//! // Relay chain slot duration, in milliseconds.
|
|
||||||
//! pub const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000;
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! 3. Establish constants `MILLISECS_PER_BLOCK` and `SLOT_DURATION` if not already present in the
|
|
||||||
//! runtime.
|
|
||||||
//! ```ignore
|
|
||||||
//! // `SLOT_DURATION` is picked up by `pezpallet_timestamp` which is in turn picked
|
|
||||||
//! // up by `pezpallet_aura` to implement `fn slot_duration()`.
|
|
||||||
//! //
|
|
||||||
//! // Change this to adjust the block time.
|
|
||||||
//! pub const MILLISECS_PER_BLOCK: u64 = 12000;
|
|
||||||
//! pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! 4. Configure `pezcumulus_pezpallet_teyrchain_system` in the runtime.
|
|
||||||
//!
|
|
||||||
//! - Define a `FixedVelocityConsensusHook` using our capacity, velocity, and relay slot duration
|
|
||||||
//! constants. Use this to set the teyrchain system `ConsensusHook` property.
|
|
||||||
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/lib.rs", ConsensusHook)]
|
|
||||||
//! ```ignore
|
|
||||||
//! impl pezcumulus_pezpallet_teyrchain_system::Config for Runtime {
|
|
||||||
//! ..
|
|
||||||
//! type ConsensusHook = ConsensusHook;
|
|
||||||
//! ..
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//! - Set the teyrchain system property `CheckAssociatedRelayNumber` to
|
|
||||||
//! `RelayNumberMonotonicallyIncreases`
|
|
||||||
//! ```ignore
|
|
||||||
//! impl pezcumulus_pezpallet_teyrchain_system::Config for Runtime {
|
|
||||||
//! ..
|
|
||||||
//! type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases;
|
|
||||||
//! ..
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! 5. Configure `pezpallet_aura` in the runtime.
|
|
||||||
//!
|
|
||||||
//! - Set `AllowMultipleBlocksPerSlot` to `false` (don't worry, we will set it to `true` when we
|
|
||||||
//! activate async backing in phase 3).
|
|
||||||
//!
|
|
||||||
//! - Define `pezpallet_aura::SlotDuration` using our constant `SLOT_DURATION`
|
|
||||||
//! ```ignore
|
|
||||||
//! impl pezpallet_aura::Config for Runtime {
|
|
||||||
//! ..
|
|
||||||
//! type AllowMultipleBlocksPerSlot = ConstBool<false>;
|
|
||||||
//! #[cfg(feature = "experimental")]
|
|
||||||
//! type SlotDuration = ConstU64<SLOT_DURATION>;
|
|
||||||
//! ..
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! 6. Update `pezsp_consensus_aura::AuraApi::slot_duration` in `pezsp_api::impl_runtime_apis` to
|
|
||||||
//! match the constant `SLOT_DURATION`
|
|
||||||
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/apis.rs", impl_slot_duration)]
|
|
||||||
//!
|
|
||||||
//! 7. Implement the `AuraUnincludedSegmentApi`, which allows the collator client to query its
|
|
||||||
//! runtime to determine whether it should author a block.
|
|
||||||
//!
|
|
||||||
//! - Add the dependency `pezcumulus-primitives-aura` to the `runtime/Cargo.toml` file for your
|
|
||||||
//! runtime
|
|
||||||
//! ```ignore
|
|
||||||
//! ..
|
|
||||||
//! pezcumulus-primitives-aura = { path = "../../../../primitives/aura", default-features = false }
|
|
||||||
//! ..
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! - In the same file, add `"pezcumulus-primitives-aura/std",` to the `std` feature.
|
|
||||||
//!
|
|
||||||
//! - Inside the `impl_runtime_apis!` block for your runtime, implement the
|
|
||||||
//! `pezcumulus_primitives_aura::AuraUnincludedSegmentApi` as shown below.
|
|
||||||
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/apis.rs", impl_can_build_upon)]
|
|
||||||
//!
|
|
||||||
//! **Note:** With a capacity of 1 we have an effective velocity of ½ even when velocity is
|
|
||||||
//! configured to some larger value. This is because capacity will be filled after a single block is
|
|
||||||
//! produced and will only be freed up after that block is included on the relay chain, which takes
|
|
||||||
//! 2 relay blocks to accomplish. Thus with capacity 1 and velocity 1 we get the customary 12 second
|
|
||||||
//! teyrchain block time.
|
|
||||||
//!
|
|
||||||
//! 8. If your `runtime/src/lib.rs` provides a `CheckInherents` type to `register_validate_block`,
|
|
||||||
//! remove it. `FixedVelocityConsensusHook` makes it unnecessary. The following example shows how
|
|
||||||
//! `register_validate_block` should look after removing `CheckInherents`.
|
|
||||||
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/lib.rs", register_validate_block)]
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//! ## Phase 2 - Update Teyrchain Nodes
|
|
||||||
//!
|
|
||||||
//! This phase consists of plugging in the new lookahead collator node.
|
|
||||||
//!
|
|
||||||
//! 1. Import `pezcumulus_primitives_core::ValidationCode` to `node/src/service.rs`.
|
|
||||||
#![doc = docify::embed!("../../templates/teyrchain/node/src/service.rs", pezcumulus_primitives)]
|
|
||||||
//!
|
|
||||||
//! 2. In `node/src/service.rs`, modify `pezsc_service::spawn_tasks` to use a clone of `Backend`
|
|
||||||
//! rather than the original
|
|
||||||
//! ```ignore
|
|
||||||
//! pezsc_service::spawn_tasks(pezsc_service::SpawnTasksParams {
|
|
||||||
//! ..
|
|
||||||
//! backend: backend.clone(),
|
|
||||||
//! ..
|
|
||||||
//! })?;
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! 3. Add `backend` as a parameter to `start_consensus()` in `node/src/service.rs`
|
|
||||||
//! ```text
|
|
||||||
//! fn start_consensus(
|
|
||||||
//! ..
|
|
||||||
//! backend: Arc<TeyrchainBackend>,
|
|
||||||
//! ..
|
|
||||||
//! ```
|
|
||||||
//! ```ignore
|
|
||||||
//! if validator {
|
|
||||||
//! start_consensus(
|
|
||||||
//! ..
|
|
||||||
//! backend.clone(),
|
|
||||||
//! ..
|
|
||||||
//! )?;
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! 4. In `node/src/service.rs` import the lookahead collator rather than the basic collator
|
|
||||||
#![doc = docify::embed!("../../templates/teyrchain/node/src/service.rs", lookahead_collator)]
|
|
||||||
//!
|
|
||||||
//! 5. In `start_consensus()` replace the `BasicAuraParams` struct with `AuraParams`
|
|
||||||
//! - Change the struct type from `BasicAuraParams` to `AuraParams`
|
|
||||||
//! - In the `para_client` field, pass in a cloned para client rather than the original
|
|
||||||
//! - Add a `para_backend` parameter after `para_client`, passing in our para backend
|
|
||||||
//! - Provide a `code_hash_provider` closure like that shown below
|
|
||||||
//! - Increase `authoring_duration` from 500 milliseconds to 2000
|
|
||||||
//! ```ignore
|
|
||||||
//! let params = AuraParams {
|
|
||||||
//! ..
|
|
||||||
//! para_client: client.clone(),
|
|
||||||
//! para_backend: backend.clone(),
|
|
||||||
//! ..
|
|
||||||
//! code_hash_provider: move |block_hash| {
|
|
||||||
//! client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash())
|
|
||||||
//! },
|
|
||||||
//! ..
|
|
||||||
//! authoring_duration: Duration::from_millis(2000),
|
|
||||||
//! ..
|
|
||||||
//! };
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! **Note:** Set `authoring_duration` to whatever you want, taking your own hardware into account.
|
|
||||||
//! But if the backer who should be slower than you due to reading from disk, times out at two
|
|
||||||
//! seconds your candidates will be rejected.
|
|
||||||
//!
|
|
||||||
//! 6. In `start_consensus()` replace `basic_aura::run` with `aura::run`
|
|
||||||
//! ```ignore
|
|
||||||
//! let fut =
|
|
||||||
//! aura::run::<Block, pezsp_consensus_aura::sr25519::AuthorityPair, _, _, _, _, _, _, _, _, _>(
|
|
||||||
//! params,
|
|
||||||
//! );
|
|
||||||
//! task_manager.spawn_essential_handle().spawn("aura", None, fut);
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! ## Phase 3 - Activate Async Backing
|
|
||||||
//!
|
|
||||||
//! This phase consists of changes to your teyrchain’s runtime that activate async backing feature.
|
|
||||||
//!
|
|
||||||
//! 1. Configure `pezpallet_aura`, setting `AllowMultipleBlocksPerSlot` to true in
|
|
||||||
//! `runtime/src/lib.rs`.
|
|
||||||
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/configs/mod.rs", aura_config)]
|
|
||||||
//!
|
|
||||||
//! 2. Increase the maximum `UNINCLUDED_SEGMENT_CAPACITY` in `runtime/src/lib.rs`.
|
|
||||||
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/lib.rs", async_backing_params)]
|
|
||||||
//!
|
|
||||||
//! 3. Decrease `MILLISECS_PER_BLOCK` to 6000.
|
|
||||||
//!
|
|
||||||
//! - Note: For a teyrchain which measures time in terms of its own block number rather than by
|
|
||||||
//! relay block number it may be preferable to increase velocity. Changing block time may cause
|
|
||||||
//! complications, requiring additional changes. See the section “Timing by Block Number”.
|
|
||||||
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/lib.rs", block_times)]
|
|
||||||
//!
|
|
||||||
//! 4. Update `MAXIMUM_BLOCK_WEIGHT` to reflect the increased time available for block production.
|
|
||||||
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/lib.rs", max_block_weight)]
|
|
||||||
//!
|
|
||||||
//! 5. Add a feature flagged alternative for `MinimumPeriod` in `pezpallet_timestamp`. The type
|
|
||||||
//! should be `ConstU64<0>` with the feature flag experimental, and `ConstU64<{SLOT_DURATION /
|
|
||||||
//! 2}>` without.
|
|
||||||
//! ```ignore
|
|
||||||
//! impl pezpallet_timestamp::Config for Runtime {
|
|
||||||
//! ..
|
|
||||||
//! #[cfg(feature = "experimental")]
|
|
||||||
//! type MinimumPeriod = ConstU64<0>;
|
|
||||||
//! #[cfg(not(feature = "experimental"))]
|
|
||||||
//! type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>;
|
|
||||||
//! ..
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! ## Timing by Block Number
|
|
||||||
//!
|
|
||||||
//! With asynchronous backing it will be possible for teyrchains to opt for a block time of 6
|
|
||||||
//! seconds rather than 12 seconds. But modifying block duration isn’t so simple for a teyrchain
|
|
||||||
//! which was measuring time in terms of its own block number. It could result in expected and
|
|
||||||
//! actual time not matching up, stalling the teyrchain.
|
|
||||||
//!
|
|
||||||
//! One strategy to deal with this issue is to instead rely on relay chain block numbers for timing.
|
|
||||||
//! Relay block number is kept track of by each teyrchain in `pezpallet-teyrchain-system` with the
|
|
||||||
//! storage value `LastRelayChainBlockNumber`. This value can be obtained and used wherever timing
|
|
||||||
//! based on block number is needed.
|
|
||||||
|
|
||||||
#![deny(rustdoc::broken_intra_doc_links)]
|
|
||||||
#![deny(rustdoc::private_intra_doc_links)]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//! # Changing Consensus
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
//! # Enable elastic scaling for a teyrchain
|
|
||||||
//!
|
|
||||||
//! <div class="warning">This guide assumes full familiarity with Asynchronous Backing and its
|
|
||||||
//! terminology, as defined in <a href="https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/guides/async_backing_guide/index.html">the Pezkuwi SDK Docs</a>.
|
|
||||||
//! </div>
|
|
||||||
//!
|
|
||||||
//! ## Quick introduction to Elastic Scaling
|
|
||||||
//!
|
|
||||||
//! [Elastic scaling](https://www.parity.io/blog/polkadot-web3-cloud) is a feature that enables teyrchains (rollups) to use multiple cores.
|
|
||||||
//! Teyrchains can adjust their usage of core resources on the fly to increase TPS and decrease
|
|
||||||
//! latency.
|
|
||||||
//!
|
|
||||||
//! ### When do you need Elastic Scaling?
|
|
||||||
//!
|
|
||||||
//! Depending on their use case, applications might have an increased need for the following:
|
|
||||||
//! - compute (CPU weight)
|
|
||||||
//! - bandwidth (proof size)
|
|
||||||
//! - lower latency (block time)
|
|
||||||
//!
|
|
||||||
//! ### High throughput (TPS) and lower latency
|
|
||||||
//!
|
|
||||||
//! If the main bottleneck is the CPU, then your teyrchain needs to maximize the compute usage of
|
|
||||||
//! each core while also achieving a lower latency.
|
|
||||||
//! 3 cores provide the best balance between CPU, bandwidth and latency: up to 6s of execution,
|
|
||||||
//! 5MB/s of DA bandwidth and fast block time of just 2 seconds.
|
|
||||||
//!
|
|
||||||
//! ### High bandwidth
|
|
||||||
//!
|
|
||||||
//! Useful for applications that are bottlenecked by bandwidth.
|
|
||||||
//! By using 6 cores, applications can make use of up to 6s of compute, 10MB/s of bandwidth
|
|
||||||
//! while also achieving 1 second block times.
|
|
||||||
//!
|
|
||||||
//! ### Ultra low latency
|
|
||||||
//!
|
|
||||||
//! When latency is the primary requirement, Elastic scaling is currently the only solution. The
|
|
||||||
//! caveat is the efficiency of core time usage decreases as more cores are used.
|
|
||||||
//!
|
|
||||||
//! For example, using 12 cores enables fast transaction confirmations with 500ms blocks and up to
|
|
||||||
//! 20 MB/s of DA bandwidth.
|
|
||||||
//!
|
|
||||||
//! ## Dependencies
|
|
||||||
//!
|
|
||||||
//! Prerequisites: Pezkuwi-SDK `2509` or newer.
|
|
||||||
//!
|
|
||||||
//! To ensure the security and reliability of your chain when using this feature you need the
|
|
||||||
//! following:
|
|
||||||
//! - An omni-node based collator. This has already become the default choice for collators.
|
|
||||||
//! - UMP signal support.
|
|
||||||
//! [RFC103](https://github.com/polkadot-fellows/RFCs/blob/main/text/0103-introduce-core-index-commitment.md).
|
|
||||||
//! This is mandatory protection against PoV replay attacks.
|
|
||||||
//! - Enabling the relay parent offset feature. This is required to ensure the teyrchain block times
|
|
||||||
//! and transaction in-block confidence are not negatively affected by relay chain forks. Read
|
|
||||||
//! [`crate::guides::handling_teyrchain_forks`] for more information.
|
|
||||||
//! - Block production configuration adjustments.
|
|
||||||
//!
|
|
||||||
//! ### Upgrade to Pezkuwi Omni node
|
|
||||||
//!
|
|
||||||
//! Your collators need to run `pezkuwi-teyrchain` or `pezkuwi-omni-node` with the `--authoring
|
|
||||||
//! slot-based` CLI argument.
|
|
||||||
//! To avoid potential issues and get best performance it is recommeneded to always run the
|
|
||||||
//! latest release on all of the collators.
|
|
||||||
//!
|
|
||||||
//! Further information about omni-node and how to upgrade is available:
|
|
||||||
//! - [high level docs](https://docs.pezkuwichain.io/develop/toolkit/parachains/polkadot-omni-node/)
|
|
||||||
//! - [`crate::reference_docs::omni_node`]
|
|
||||||
//!
|
|
||||||
//! ### UMP signals
|
|
||||||
//!
|
|
||||||
//! UMP signals are now enabled by default in the `teyrchain-system` pezpallet and are used for
|
|
||||||
//! elastic scaling. You can find more technical details about UMP signals and their usage for
|
|
||||||
//! elastic scaling
|
|
||||||
//! [here](https://github.com/polkadot-fellows/RFCs/blob/main/text/0103-introduce-core-index-commitment.md).
|
|
||||||
//!
|
|
||||||
//! ### Enable the relay parent offset feature
|
|
||||||
//!
|
|
||||||
//! It is recommended to use an offset of `1`, which is sufficient to eliminate any issues
|
|
||||||
//! with relay chain forks.
|
|
||||||
//!
|
|
||||||
//! Configure the relay parent offset like this:
|
|
||||||
//! ```ignore
|
|
||||||
//! /// Build with an offset of 1 behind the relay chain best block.
|
|
||||||
//! const RELAY_PARENT_OFFSET: u32 = 1;
|
|
||||||
//!
|
|
||||||
//! impl pezcumulus_pezpallet_teyrchain_system::Config for Runtime {
|
|
||||||
//! // ...
|
|
||||||
//! type RelayParentOffset = ConstU32<RELAY_PARENT_OFFSET>;
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! Implement the runtime API to retrieve the offset on the client side.
|
|
||||||
//! ```ignore
|
|
||||||
//! impl pezcumulus_primitives_core::RelayParentOffsetApi<Block> for Runtime {
|
|
||||||
//! fn relay_parent_offset() -> u32 {
|
|
||||||
//! RELAY_PARENT_OFFSET
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! ### Block production configuration
|
|
||||||
//!
|
|
||||||
//! This configuration directly controls the minimum block time and maximum number of cores
|
|
||||||
//! the teyrchain can use.
|
|
||||||
//!
|
|
||||||
//! Example configuration for a 3 core teyrchain:
|
|
||||||
//! ```ignore
|
|
||||||
//! /// The upper limit of how many teyrchain blocks are processed by the relay chain per
|
|
||||||
//! /// parent. Limits the number of blocks authored per slot. This determines the minimum
|
|
||||||
//! /// block time of the teyrchain:
|
|
||||||
//! /// `RELAY_CHAIN_SLOT_DURATION_MILLIS/BLOCK_PROCESSING_VELOCITY`
|
|
||||||
//! const BLOCK_PROCESSING_VELOCITY: u32 = 3;
|
|
||||||
//!
|
|
||||||
//! /// Maximum number of blocks simultaneously accepted by the Runtime, not yet included
|
|
||||||
//! /// into the relay chain.
|
|
||||||
//! const UNINCLUDED_SEGMENT_CAPACITY: u32 = (2 + RELAY_PARENT_OFFSET) *
|
|
||||||
//! BLOCK_PROCESSING_VELOCITY + 1;
|
|
||||||
//!
|
|
||||||
//! /// Relay chain slot duration, in milliseconds.
|
|
||||||
//! const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000;
|
|
||||||
//!
|
|
||||||
//! type ConsensusHook = pezcumulus_pezpallet_aura_ext::FixedVelocityConsensusHook<
|
|
||||||
//! Runtime,
|
|
||||||
//! RELAY_CHAIN_SLOT_DURATION_MILLIS,
|
|
||||||
//! BLOCK_PROCESSING_VELOCITY,
|
|
||||||
//! UNINCLUDED_SEGMENT_CAPACITY,
|
|
||||||
//! >;
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! ### Teyrchain Slot Duration
|
|
||||||
//!
|
|
||||||
//! A common source of confusion is the correct configuration of the `SlotDuration` that is passed
|
|
||||||
//! to `pezpallet-aura`.
|
|
||||||
//! ```ignore
|
|
||||||
//! impl pezpallet_aura::Config for Runtime {
|
|
||||||
//! // ...
|
|
||||||
//! type SlotDuration = ConstU64<SLOT_DURATION>;
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! The slot duration determines the length of each author's turn and is decoupled from the block
|
|
||||||
//! production interval. During their slot, authors are allowed to produce multiple blocks. **The
|
|
||||||
//! slot duration is required to be at least 6s (same as on the relay chain).**
|
|
||||||
//!
|
|
||||||
//! **Configuration recommendations:**
|
|
||||||
//! - For new teyrchains starting from genesis: use a slot duration of 24 seconds
|
|
||||||
//! - For existing live teyrchains: leave the slot duration unchanged
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//! ## Current limitations
|
|
||||||
//!
|
|
||||||
//! ### Maximum execution time per relay chain block.
|
|
||||||
//!
|
|
||||||
//! Since teyrchain block authoring is sequential, the next block can only be built after
|
|
||||||
//! the previous one has been imported.
|
|
||||||
//! At present, a core allows up to 2 seconds of execution per relay chain block.
|
|
||||||
//!
|
|
||||||
//! If we assume a 6s teyrchain slot, and each block takes the full 2 seconds to execute,
|
|
||||||
//! the teyrchain will not be able to fully utilize the compute resources of all 3 cores.
|
|
||||||
//!
|
|
||||||
//! If the collator hardware is faster, it can author and import full blocks more quickly,
|
|
||||||
//! making it possible to utilize even more than 3 cores efficiently.
|
|
||||||
//!
|
|
||||||
//! #### Why?
|
|
||||||
//!
|
|
||||||
//! Within a 6-second teyrchain slot, collators can author multiple teyrchain blocks.
|
|
||||||
//! Before building the first block in a slot, the new block author must import the last
|
|
||||||
//! block produced by the previous author.
|
|
||||||
//! If the import of the last block is not completed before the next relay chain slot starts,
|
|
||||||
//! the new author will build on its parent (assuming it was imported). This will create a fork
|
|
||||||
//! which degrades the teyrchain block confidence and block times.
|
|
||||||
//!
|
|
||||||
//! This means that, on reference hardware, a teyrchain with a slot time of 6s can
|
|
||||||
//! effectively utilize up to 4 seconds of execution per relay chain block, because it needs to
|
|
||||||
//! ensure the next block author has enough time to import the last block.
|
|
||||||
//! Hardware with higher single-core performance can enable a teyrchain to fully utilize more
|
|
||||||
//! cores.
|
|
||||||
//!
|
|
||||||
//! ### Fixed factor scaling.
|
|
||||||
//!
|
|
||||||
//! For true elasticity, a teyrchain needs to acquire more cores when needed in an automated
|
|
||||||
//! manner. This functionality is not yet available in the SDK, thus acquiring additional
|
|
||||||
//! on-demand or bulk cores has to be managed externally.
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
//! # Enable metadata hash verification
|
|
||||||
//!
|
|
||||||
//! This guide will teach you how to enable the metadata hash verification in your runtime.
|
|
||||||
//!
|
|
||||||
//! ## What is metadata hash verification?
|
|
||||||
//!
|
|
||||||
//! Each FRAME based runtime exposes metadata about itself. This metadata is used by consumers of
|
|
||||||
//! the runtime to interpret the state, to construct transactions etc. Part of this metadata are the
|
|
||||||
//! type information. These type information can be used to e.g. decode storage entries or to decode
|
|
||||||
//! a transaction. So, the metadata is quite useful for wallets to interact with a FRAME based
|
|
||||||
//! chain. Online wallets can fetch the metadata directly from any node of the chain they are
|
|
||||||
//! connected to, but offline wallets can not do this. So, for the offline wallet to have access to
|
|
||||||
//! the metadata it needs to be transferred and stored on the device. The problem is that the
|
|
||||||
//! metadata has a size of several hundreds of kilobytes, which takes quite a while to transfer to
|
|
||||||
//! these offline wallets and the internal storage of these devices is also not big enough to store
|
|
||||||
//! the metadata for one or more networks. The next problem is that the offline wallet/user can not
|
|
||||||
//! trust the metadata to be correct. It is very important for the metadata to be correct or
|
|
||||||
//! otherwise an attacker could change them in a way that the offline wallet decodes a transaction
|
|
||||||
//! in a different way than what it will be decoded to on chain. So, the user may sign an incorrect
|
|
||||||
//! transaction leading to unexpected behavior.
|
|
||||||
//!
|
|
||||||
//! The metadata hash verification circumvents the issues of the huge metadata and the need to trust
|
|
||||||
//! some metadata blob to be correct. To generate a hash for the metadata, the metadata is chunked,
|
|
||||||
//! these chunks are put into a merkle tree and then the root of this merkle tree is the "metadata
|
|
||||||
//! hash". For a more technical explanation on how it works, see
|
|
||||||
//! [RFC78](https://polkadot-fellows.github.io/RFCs/approved/0078-merkleized-metadata.html). At compile
|
|
||||||
//! time the metadata hash is generated and "baked" into the runtime. This makes it extremely cheap
|
|
||||||
//! for the runtime to verify on chain that the metadata hash is correct. By having the runtime
|
|
||||||
//! verify the hash on chain, the user also doesn't need to trust the offchain metadata. If the
|
|
||||||
//! metadata hash doesn't match the on chain metadata hash the transaction will be rejected. The
|
|
||||||
//! metadata hash itself is added to the data of the transaction that is signed, this means the
|
|
||||||
//! actual hash does not appear in the transaction. On chain the same procedure is repeated with the
|
|
||||||
//! metadata hash that is known by the runtime and if the metadata hash doesn't match the signature
|
|
||||||
//! verification will fail. As the metadata hash is actually the root of a merkle tree, the offline
|
|
||||||
//! wallet can get proofs of individual types to decode a transaction. This means that the offline
|
|
||||||
//! wallet does not require the entire metadata to be present on the device.
|
|
||||||
//!
|
|
||||||
//! ## Integrating metadata hash verification into your runtime
|
|
||||||
//!
|
|
||||||
//! The integration of the metadata hash verification is split into two parts, first the actual
|
|
||||||
//! integration into the runtime and secondly the enabling of the metadata hash generation at
|
|
||||||
//! compile time.
|
|
||||||
//!
|
|
||||||
//! ### Runtime integration
|
|
||||||
//!
|
|
||||||
//! From the runtime side only the
|
|
||||||
//! [`CheckMetadataHash`](pezframe_metadata_hash_extension::CheckMetadataHash) needs to be added to
|
|
||||||
//! the list of signed extension:
|
|
||||||
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/lib.rs", template_signed_extra)]
|
|
||||||
//!
|
|
||||||
//! > **Note:**
|
|
||||||
//! >
|
|
||||||
//! > Adding the signed extension changes the encoding of the transaction and adds one extra byte
|
|
||||||
//! > per transaction!
|
|
||||||
//!
|
|
||||||
//! This signed extension will make sure to decode the requested `mode` and will add the metadata
|
|
||||||
//! hash to the signed data depending on the requested `mode`. The `mode` gives the user/wallet
|
|
||||||
//! control over deciding if the metadata hash should be verified or not. The metadata hash itself
|
|
||||||
//! is drawn from the `RUNTIME_METADATA_HASH` environment variable. If the environment variable is
|
|
||||||
//! not set, any transaction that requires the metadata hash is rejected with the error
|
|
||||||
//! `CannotLookup`. This is a security measurement to prevent including invalid transactions.
|
|
||||||
//!
|
|
||||||
//! <div class="warning">
|
|
||||||
//!
|
|
||||||
//! The extension does not work with the native runtime, because the
|
|
||||||
//! `RUNTIME_METADATA_HASH` environment variable is not set when building the
|
|
||||||
//! `pezframe-metadata-hash-extension` crate.
|
|
||||||
//!
|
|
||||||
//! </div>
|
|
||||||
//!
|
|
||||||
//! ### Enable metadata hash generation
|
|
||||||
//!
|
|
||||||
//! The metadata hash generation needs to be enabled when building the wasm binary. The
|
|
||||||
//! `bizinikiwi-wasm-builder` supports this out of the box:
|
|
||||||
#![doc = docify::embed!("../../templates/teyrchain/runtime/build.rs", template_enable_metadata_hash)]
|
|
||||||
//!
|
|
||||||
//! > **Note:**
|
|
||||||
//! >
|
|
||||||
//! > The `metadata-hash` feature needs to be enabled for the `bizinikiwi-wasm-builder` to enable
|
|
||||||
//! > the
|
|
||||||
//! > code for being able to generate the metadata hash. It is also recommended to put the metadata
|
|
||||||
//! > hash generation behind a feature in the runtime as shown above. The reason behind is that it
|
|
||||||
//! > adds a lot of code which increases the compile time and the generation itself also increases
|
|
||||||
//! > the compile time. Thus, it is recommended to enable the feature only when the metadata hash is
|
|
||||||
//! > required (e.g. for an on-chain build).
|
|
||||||
//!
|
|
||||||
//! The two parameters to `enable_metadata_hash` are the token symbol and the number of decimals of
|
|
||||||
//! the primary token of the chain. These information are included for the wallets to show token
|
|
||||||
//! related operations in a more user friendly way.
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
//! # Enable storage weight reclaiming
|
|
||||||
//!
|
|
||||||
//! This guide will teach you how to enable storage weight reclaiming for a teyrchain. The
|
|
||||||
//! explanations in this guide assume a project structure similar to the one detailed in
|
|
||||||
//! the [bizinikiwi documentation](crate::pezkuwi_sdk::bizinikiwi#anatomy-of-a-binary-crate). Full
|
|
||||||
//! technical details are available in the original [pull request](https://github.com/pezkuwichain/pezkuwi-sdk/issues/257).
|
|
||||||
//!
|
|
||||||
//! # What is PoV reclaim?
|
|
||||||
//! When a teyrchain submits a block to a relay chain like Pezkuwi or Dicle, it sends the block
|
|
||||||
//! itself and a storage proof. Together they form the Proof-of-Validity (PoV). The PoV allows the
|
|
||||||
//! relay chain to validate the teyrchain block by re-executing it. Relay chain
|
|
||||||
//! validators distribute this PoV among themselves over the network. This distribution is costly
|
|
||||||
//! and limits the size of the storage proof. The storage weight dimension of FRAME weights reflects
|
|
||||||
//! this cost and limits the size of the storage proof. However, the storage weight determined
|
|
||||||
//! during [benchmarking](crate::reference_docs::pezframe_benchmarking_weight) represents the worst
|
|
||||||
//! case. In reality, runtime operations often consume less space in the storage proof. PoV reclaim
|
|
||||||
//! offers a mechanism to reclaim the difference between the benchmarked worst-case and the real
|
|
||||||
//! proof-size consumption.
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//! # How to enable PoV reclaim
|
|
||||||
//! ## 1. Add the host function to your node
|
|
||||||
//!
|
|
||||||
//! To reclaim excess storage weight, a teyrchain runtime needs the
|
|
||||||
//! ability to fetch the size of the storage proof from the node. The reclaim
|
|
||||||
//! mechanism uses the
|
|
||||||
//! [`storage_proof_size`](pezcumulus_primitives_proof_size_hostfunction::storage_proof_size)
|
|
||||||
//! host function for this purpose. For convenience, pezcumulus provides
|
|
||||||
//! [`TeyrchainHostFunctions`](pezcumulus_client_service::TeyrchainHostFunctions), a set of
|
|
||||||
//! host functions typically used by pezcumulus-based teyrchains. In the binary crate of your
|
|
||||||
//! teyrchain, find the instantiation of the [`WasmExecutor`](pezsc_executor::WasmExecutor) and set
|
|
||||||
//! the correct generic type.
|
|
||||||
//!
|
|
||||||
//! This example from the teyrchain-template shows a type definition that includes the correct
|
|
||||||
//! host functions.
|
|
||||||
#![doc = docify::embed!("../../templates/teyrchain/node/src/service.rs", wasm_executor)]
|
|
||||||
//!
|
|
||||||
//! > **Note:**
|
|
||||||
//! >
|
|
||||||
//! > If you see error `runtime requires function imports which are not present on the host:
|
|
||||||
//! > 'env:ext_storage_proof_size_storage_proof_size_version_1'`, it is likely
|
|
||||||
//! > that this step in the guide was not set up correctly.
|
|
||||||
//!
|
|
||||||
//! ## 2. Enable storage proof recording during import
|
|
||||||
//!
|
|
||||||
//! The reclaim mechanism reads the size of the currently recorded storage proof multiple times
|
|
||||||
//! during block authoring and block import. Proof recording during authoring is already enabled on
|
|
||||||
//! teyrchains. You must also ensure that storage proof recording is enabled during block import.
|
|
||||||
//! Find where your node builds the fundamental bizinikiwi components by calling
|
|
||||||
//! [`new_full_parts`](pezsc_service::new_full_parts). Replace this
|
|
||||||
//! with [`new_full_parts_record_import`](pezsc_service::new_full_parts_record_import) and
|
|
||||||
//! pass `true` as the last parameter to enable import recording.
|
|
||||||
#![doc = docify::embed!("../../templates/teyrchain/node/src/service.rs", component_instantiation)]
|
|
||||||
//!
|
|
||||||
//! > **Note:**
|
|
||||||
//! >
|
|
||||||
//! > If you see error `Storage root must match that calculated.` during block import, it is likely
|
|
||||||
//! > that this step in the guide was not
|
|
||||||
//! > set up correctly.
|
|
||||||
//!
|
|
||||||
//! ## 3. Add the TransactionExtension to your runtime
|
|
||||||
//!
|
|
||||||
//! In your runtime, you will find a list of TransactionExtensions.
|
|
||||||
//! To enable the reclaiming,
|
|
||||||
//! set [`StorageWeightReclaim`](pezcumulus_pezpallet_weight_reclaim::StorageWeightReclaim)
|
|
||||||
//! as a warpper of that list.
|
|
||||||
//! It is necessary that this extension wraps all the other transaction extensions in order to catch
|
|
||||||
//! the whole PoV size of the transactions.
|
|
||||||
//! The extension will check the size of the storage proof before and after an extrinsic execution.
|
|
||||||
//! It reclaims the difference between the calculated size and the benchmarked size.
|
|
||||||
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/lib.rs", template_signed_extra)]
|
|
||||||
//!
|
|
||||||
//! ## Optional: Verify that reclaim works
|
|
||||||
//!
|
|
||||||
//! Start your node with the log target `runtime::storage_reclaim` set to `trace` to enable full
|
|
||||||
//! logging for `StorageWeightReclaim`. The following log is an example from a local testnet. To
|
|
||||||
//! trigger the log, execute any extrinsic on the network.
|
|
||||||
//!
|
|
||||||
//! ```ignore
|
|
||||||
//! ...
|
|
||||||
//! 2024-04-22 17:31:48.014 TRACE runtime::storage_reclaim: [ferdie] Reclaiming storage weight. benchmarked: 3593, consumed: 265 unspent: 0
|
|
||||||
//! ...
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! In the above example we see a benchmarked size of 3593 bytes, while the extrinsic only consumed
|
|
||||||
//! 265 bytes of proof size. This results in 3328 bytes of reclaim.
|
|
||||||
#![deny(rustdoc::broken_intra_doc_links)]
|
|
||||||
#![deny(rustdoc::private_intra_doc_links)]
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
//! # Teyrchain forks
|
|
||||||
//!
|
|
||||||
//! In this guide, we will examine how AURA-based teyrchains handle forks. AURA (Authority Round) is
|
|
||||||
//! a consensus mechanism where block authors rotate at fixed time intervals. Each author gets a
|
|
||||||
//! predetermined time slice during which they are allowed to author a block. On its own, this
|
|
||||||
//! mechanism is fork-free.
|
|
||||||
//!
|
|
||||||
//! However, since the relay chain provides security and serves as the source of truth for
|
|
||||||
//! teyrchains, the teyrchain is dependent on it. This relationship can introduce complexities that
|
|
||||||
//! lead to forking scenarios.
|
|
||||||
//!
|
|
||||||
//! ## Background
|
|
||||||
//! Each teyrchain block has a relay parent, which is a relay chain block that provides context to
|
|
||||||
//! our teyrchain block. The constraints the relay chain imposes on our teyrchain can cause forks
|
|
||||||
//! under certain conditions. With asynchronous-backing enabled chains, the node side is building
|
|
||||||
//! blocks on all relay chain forks. This means that no matter which fork of the relay chain
|
|
||||||
//! ultimately progressed, the teyrchain would have a block ready for that fork. The situation
|
|
||||||
//! changes when teyrchains want to produce blocks at a faster cadence. In a scenario where a
|
|
||||||
//! teyrchain might author on 3 cores with elastic scaling, it is not possible to author on all
|
|
||||||
//! relay chain forks. The time constraints do not allow it. Building on two forks would result in 6
|
|
||||||
//! blocks. The authoring of these blocks would consume more time than we have available before the
|
|
||||||
//! next relay chain block arrives. This limitation requires a more fork-resistant approach to
|
|
||||||
//! block-building.
|
|
||||||
//!
|
|
||||||
//! ## Impact of Forks
|
|
||||||
//! When a relay chain fork occurs and the teyrchain builds on a fork that will not be extended in
|
|
||||||
//! the future, the blocks built on that fork are lost and need to be rebuilt. This increases
|
|
||||||
//! latency and reduces throughput, affecting the overall performance of the teyrchain.
|
|
||||||
//!
|
|
||||||
//! # Building on Older Pelay Parents
|
|
||||||
//! Pezcumulus offers a way to mitigate the occurence of forks. Instead of picking a block at the
|
|
||||||
//! tip of the relay chain to build blocks, the node side can pick a relay chain block that is
|
|
||||||
//! older. By building on 12s old relay chain blocks, forks will already have settled and the
|
|
||||||
//! teyrchain can build fork-free.
|
|
||||||
//!
|
|
||||||
//! ```text
|
|
||||||
//! Without offset:
|
|
||||||
//! Relay Chain: A --- B --- C --- D --- E
|
|
||||||
//! \
|
|
||||||
//! --- D' --- E'
|
|
||||||
//! Teyrchain: X --- Y --- ? (builds on both D and D', wasting resources)
|
|
||||||
//!
|
|
||||||
//! With offset (2 blocks):
|
|
||||||
//! Relay Chain: A --- B --- C --- D --- E
|
|
||||||
//! \
|
|
||||||
//! --- D' --- E'
|
|
||||||
//! Teyrchain: X(A) - Y (B) - Z (on C, fork already resolved)
|
|
||||||
//! ```
|
|
||||||
//! **Note:** It is possible that relay chain forks extend over more than 1-2 blocks. However, it is
|
|
||||||
//! unlikely.
|
|
||||||
//! ## Tradeoffs
|
|
||||||
//! Fork-free teyrchains come with a few tradeoffs:
|
|
||||||
//! - The latency of incoming XCM messages will be delayed by `N * 6s`, where `N` is the number of
|
|
||||||
//! relay chain blocks we want to offset by. For example, by building 2 relay chain blocks behind
|
|
||||||
//! the tip, the XCM latency will be increased by 12 seconds.
|
|
||||||
//! - The available PoV space will be slightly reduced. Assuming a 10mb PoV, teyrchains need to be
|
|
||||||
//! ready to sacrifice around 0.5% of PoV space.
|
|
||||||
//!
|
|
||||||
//! ## Enabling Guide
|
|
||||||
//! The decision whether the teyrchain should build on older relay parents is embedded into the
|
|
||||||
//! runtime. After the changes are implemented, the runtime will enforce that no author can build
|
|
||||||
//! with an offset smaller than the desired offset. If you wish to keep your current teyrchain
|
|
||||||
//! behaviour and do not want aforementioned tradeoffs, set the offset to 0.
|
|
||||||
//!
|
|
||||||
//! **Note:** The APIs mentioned here are available in `pezkuwi-sdk` versions after `stable-2506`.
|
|
||||||
//!
|
|
||||||
//! 1. Define the relay parent offset your teyrchain should respect in the runtime.
|
|
||||||
//! ```ignore
|
|
||||||
//! const RELAY_PARENT_OFFSET = 2;
|
|
||||||
//! ```
|
|
||||||
//! 2. Pass this constant to the `teyrchain-system` pezpallet.
|
|
||||||
//!
|
|
||||||
//! ```ignore
|
|
||||||
//! impl pezcumulus_pezpallet_teyrchain_system::Config for Runtime {
|
|
||||||
//! // Other config items here
|
|
||||||
//! ...
|
|
||||||
//! type RelayParentOffset = ConstU32<RELAY_PARENT_OFFSET>;
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//! 3. Implement the `RelayParentOffsetApi` runtime API for your runtime.
|
|
||||||
//!
|
|
||||||
//! ```ignore
|
|
||||||
//! impl pezcumulus_primitives_core::RelayParentOffsetApi<Block> for Runtime {
|
|
||||||
//! fn relay_parent_offset() -> u32 {
|
|
||||||
//! RELAY_PARENT_OFFSET
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//! 4. Increase the `UNINCLUDED_SEGMENT_CAPICITY` for your runtime. It needs to be increased by
|
|
||||||
//! `RELAY_PARENT_OFFSET * BLOCK_PROCESSING_VELOCITY`.
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
//! # Pezkuwi SDK Docs Guides
|
|
||||||
//!
|
|
||||||
//! This crate contains a collection of guides that are foundational to the developers of
|
|
||||||
//! Pezkuwi SDK. They are common user-journeys that are traversed in the Pezkuwi ecosystem.
|
|
||||||
//!
|
|
||||||
//! The main user-journey covered by these guides is:
|
|
||||||
//!
|
|
||||||
//! * [`your_first_pallet`], where you learn what a FRAME pezpallet is, and write your first
|
|
||||||
//! application logic.
|
|
||||||
//! * [`your_first_runtime`], where you learn how to compile your pallets into a WASM runtime.
|
|
||||||
//! * [`your_first_node`], where you learn how to run the said runtime in a node.
|
|
||||||
//!
|
|
||||||
//! > By this step, you have already launched a full Pezkuwi-SDK-based blockchain!
|
|
||||||
//!
|
|
||||||
//! Once done, feel free to step up into one of our templates: [`crate::pezkuwi_sdk::templates`].
|
|
||||||
//!
|
|
||||||
//! [`your_first_pallet`]: crate::guides::your_first_pallet
|
|
||||||
//! [`your_first_runtime`]: crate::guides::your_first_runtime
|
|
||||||
//! [`your_first_node`]: crate::guides::your_first_node
|
|
||||||
//!
|
|
||||||
//! Other guides are related to other miscellaneous topics and are listed as modules below.
|
|
||||||
|
|
||||||
/// Write your first simple pezpallet, learning the most most basic features of FRAME along the way.
|
|
||||||
pub mod your_first_pallet;
|
|
||||||
|
|
||||||
/// Write your first real [runtime](`crate::reference_docs::wasm_meta_protocol`),
|
|
||||||
/// compiling it to [WASM](crate::pezkuwi_sdk::bizinikiwi#wasm-build).
|
|
||||||
pub mod your_first_runtime;
|
|
||||||
|
|
||||||
/// Running the given runtime with a node. No specific consensus mechanism is used at this stage.
|
|
||||||
pub mod your_first_node;
|
|
||||||
|
|
||||||
/// How to enhance a given runtime and node to be pezcumulus-enabled, run it as a teyrchain
|
|
||||||
/// and connect it to a relay-chain.
|
|
||||||
// pub mod your_first_teyrchain;
|
|
||||||
|
|
||||||
/// How to enable storage weight reclaiming in a teyrchain node and runtime.
|
|
||||||
pub mod enable_pov_reclaim;
|
|
||||||
|
|
||||||
/// How to enable Async Backing on teyrchain projects that started in 2023 or before.
|
|
||||||
pub mod async_backing_guide;
|
|
||||||
|
|
||||||
/// How to enable metadata hash verification in the runtime.
|
|
||||||
pub mod enable_metadata_hash;
|
|
||||||
|
|
||||||
/// How to enable elastic scaling on a teyrchain.
|
|
||||||
pub mod enable_elastic_scaling;
|
|
||||||
|
|
||||||
/// How to parameterize teyrchain forking in relation to relay chain forking.
|
|
||||||
pub mod handling_teyrchain_forks;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//! # Pezcumulus Enabled Teyrchain
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//! # XCM Enabled Teyrchain
|
|
||||||
@@ -1,342 +0,0 @@
|
|||||||
//! # Your first Node
|
|
||||||
//!
|
|
||||||
//! In this guide, you will learn how to run a runtime, such as the one created in
|
|
||||||
//! [`your_first_runtime`], in a node. Within the context of this guide, we will focus on running
|
|
||||||
//! the runtime with an [`omni-node`]. Please first read this page to learn about the OmniNode, and
|
|
||||||
//! other options when it comes to running a node.
|
|
||||||
//!
|
|
||||||
//! [`your_first_runtime`] is a runtime with no consensus related code, and therefore can only be
|
|
||||||
//! executed with a node that also expects no consensus ([`pezsc_consensus_manual_seal`]).
|
|
||||||
//! `pezkuwi-omni-node`'s [`--dev-block-time`] precisely does this.
|
|
||||||
//!
|
|
||||||
//! > All of the following steps are coded as unit tests of this module. Please see `Source` of the
|
|
||||||
//! > page for more information.
|
|
||||||
//!
|
|
||||||
//! ## Running The Omni Node
|
|
||||||
//!
|
|
||||||
//! ### Installs
|
|
||||||
//!
|
|
||||||
//! The `pezkuwi-omni-node` can either be downloaded from the latest [Release](https://github.com/pezkuwichain/pezkuwi-sdk/releases/) of `pezkuwi-sdk`,
|
|
||||||
//! or installed using `cargo`:
|
|
||||||
//!
|
|
||||||
//! ```text
|
|
||||||
//! cargo install pezkuwi-omni-node
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! Next, we need to install the `chain-spec-builder`. This is the tool that allows us to build
|
|
||||||
//! chain-specifications, through interacting with the genesis related APIs of the runtime, as
|
|
||||||
//! described in [`crate::guides::your_first_runtime#genesis-configuration`].
|
|
||||||
//!
|
|
||||||
//! ```text
|
|
||||||
//! cargo install pezstaging-chain-spec-builder
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! > The name of the crate is prefixed with `staging` as the crate name `chain-spec-builder` on
|
|
||||||
//! > crates.io is already taken and is not controlled by `pezkuwi-sdk` developers.
|
|
||||||
//!
|
|
||||||
//! ### Building Runtime
|
|
||||||
//!
|
|
||||||
//! Next, we need to build the corresponding runtime that we wish to interact with.
|
|
||||||
//!
|
|
||||||
//! ```text
|
|
||||||
//! cargo build --release -p path-to-runtime
|
|
||||||
//! ```
|
|
||||||
//! Equivalent code in tests:
|
|
||||||
#![doc = docify::embed!("./src/guides/your_first_runtime.rs", build_runtime)]
|
|
||||||
//!
|
|
||||||
//! This creates the wasm file under `./target/{release}/wbuild/release` directory.
|
|
||||||
//!
|
|
||||||
//! ### Building Chain Spec
|
|
||||||
//!
|
|
||||||
//! Next, we can generate the corresponding chain-spec file. For this example, we will use the
|
|
||||||
//! `development` (`pezsp_genesis_config::DEVELOPMENT`) preset.
|
|
||||||
//!
|
|
||||||
//! Note that we intend to run this chain-spec with `pezkuwi-omni-node`, which is tailored for
|
|
||||||
//! running teyrchains. This requires the chain-spec to always contain the `para_id` and a
|
|
||||||
//! `relay_chain` fields, which are provided below as CLI arguments.
|
|
||||||
//!
|
|
||||||
//! ```text
|
|
||||||
//! chain-spec-builder \
|
|
||||||
//! -c <path-to-output> \
|
|
||||||
//! create \
|
|
||||||
//! --relay-chain dontcare \
|
|
||||||
//! --runtime pezkuwi_sdk_docs_first_runtime.wasm \
|
|
||||||
//! named-preset development
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! Equivalent code in tests:
|
|
||||||
#![doc = docify::embed!("./src/guides/your_first_node.rs", csb)]
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//! ### Running `pezkuwi-omni-node`
|
|
||||||
//!
|
|
||||||
//! Finally, we can run the node with the generated chain-spec file. We can also specify the block
|
|
||||||
//! time using the `--dev-block-time` flag.
|
|
||||||
//!
|
|
||||||
//! ```text
|
|
||||||
//! pezkuwi-omni-node \
|
|
||||||
//! --tmp \
|
|
||||||
//! --dev-block-time 1000 \
|
|
||||||
//! --chain <chain_spec_file>.json
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! > Note that we always prefer to use `--tmp` for testing, as it will save the chain state to a
|
|
||||||
//! > temporary folder, allowing the chain-to be easily restarted without `purge-chain`. See
|
|
||||||
//! > [`pezsc_cli::commands::PurgeChainCmd`] and [`pezsc_cli::commands::RunCmd::tmp`] for more info.
|
|
||||||
//!
|
|
||||||
//! This will start the node and import the blocks. Note while using `--dev-block-time`, the node
|
|
||||||
//! will use the testing-specific manual-seal consensus. This is an efficient way to test the
|
|
||||||
//! application logic of your runtime, without needing to yet care about consensus, block
|
|
||||||
//! production, relay-chain and so on.
|
|
||||||
//!
|
|
||||||
//! ### Next Steps
|
|
||||||
//!
|
|
||||||
//! * See the rest of the steps in [`crate::reference_docs::omni_node#user-journey`].
|
|
||||||
//!
|
|
||||||
//! [`runtime`]: crate::reference_docs::glossary#runtime
|
|
||||||
//! [`node`]: crate::reference_docs::glossary#node
|
|
||||||
//! [`build_config`]: first_runtime::Runtime#method.build_config
|
|
||||||
//! [`omni-node`]: crate::reference_docs::omni_node
|
|
||||||
//! [`--dev-block-time`]: (pezkuwi_omni_node_lib::cli::Cli::dev_block_time)
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use assert_cmd::assert::OutputAssertExt;
|
|
||||||
use cmd_lib::*;
|
|
||||||
use pezsc_chain_spec::{DEV_RUNTIME_PRESET, LOCAL_TESTNET_RUNTIME_PRESET};
|
|
||||||
use pezsp_genesis_builder::PresetId;
|
|
||||||
use rand::Rng;
|
|
||||||
use std::{
|
|
||||||
io::{BufRead, BufReader},
|
|
||||||
path::PathBuf,
|
|
||||||
process::{ChildStderr, Command, Stdio},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
const PARA_RUNTIME: &'static str = "teyrchain-template-runtime";
|
|
||||||
const CHAIN_SPEC_BUILDER: &'static str = "chain-spec-builder";
|
|
||||||
const OMNI_NODE: &'static str = "pezkuwi-omni-node";
|
|
||||||
|
|
||||||
fn cargo() -> Command {
|
|
||||||
Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_target_directory() -> Option<PathBuf> {
|
|
||||||
let output = cargo().arg("metadata").arg("--format-version=1").output().ok()?;
|
|
||||||
|
|
||||||
if !output.status.success() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let metadata: serde_json::Value = serde_json::from_slice(&output.stdout).ok()?;
|
|
||||||
let target_directory = metadata["target_directory"].as_str()?;
|
|
||||||
|
|
||||||
Some(PathBuf::from(target_directory))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_release_binary(name: &str) -> Option<PathBuf> {
|
|
||||||
let target_dir = get_target_directory()?;
|
|
||||||
let release_path = target_dir.join("release").join(name);
|
|
||||||
|
|
||||||
if release_path.exists() {
|
|
||||||
Some(release_path)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_wasm(runtime_name: &str) -> Option<PathBuf> {
|
|
||||||
let target_dir = get_target_directory()?;
|
|
||||||
let wasm_path = target_dir
|
|
||||||
.join("release")
|
|
||||||
.join("wbuild")
|
|
||||||
.join(runtime_name)
|
|
||||||
.join(format!("{}.wasm", runtime_name.replace('-', "_")));
|
|
||||||
|
|
||||||
if wasm_path.exists() {
|
|
||||||
Some(wasm_path)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn maybe_build_runtimes() {
|
|
||||||
if find_wasm(&PARA_RUNTIME).is_none() {
|
|
||||||
println!("Building teyrchain-template-runtime...");
|
|
||||||
Command::new("cargo")
|
|
||||||
.arg("build")
|
|
||||||
.arg("--release")
|
|
||||||
.arg("-p")
|
|
||||||
.arg(PARA_RUNTIME)
|
|
||||||
.assert()
|
|
||||||
.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(find_wasm(PARA_RUNTIME).is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn maybe_build_chain_spec_builder() {
|
|
||||||
if find_release_binary(CHAIN_SPEC_BUILDER).is_none() {
|
|
||||||
println!("Building chain-spec-builder...");
|
|
||||||
Command::new("cargo")
|
|
||||||
.arg("build")
|
|
||||||
.arg("--release")
|
|
||||||
.arg("-p")
|
|
||||||
.arg("pezstaging-chain-spec-builder")
|
|
||||||
.assert()
|
|
||||||
.success();
|
|
||||||
}
|
|
||||||
assert!(find_release_binary(CHAIN_SPEC_BUILDER).is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn maybe_build_omni_node() {
|
|
||||||
if find_release_binary(OMNI_NODE).is_none() {
|
|
||||||
println!("Building pezkuwi-omni-node...");
|
|
||||||
Command::new("cargo")
|
|
||||||
.arg("build")
|
|
||||||
.arg("--release")
|
|
||||||
.arg("-p")
|
|
||||||
.arg("pezkuwi-omni-node")
|
|
||||||
.assert()
|
|
||||||
.success();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn imported_block_found(stderr: ChildStderr, block: u64, timeout: u64) -> bool {
|
|
||||||
tokio::time::timeout(Duration::from_secs(timeout), async {
|
|
||||||
let want = format!("Imported #{}", block);
|
|
||||||
let reader = BufReader::new(stderr);
|
|
||||||
let mut found_block = false;
|
|
||||||
for line in reader.lines() {
|
|
||||||
if line.unwrap().contains(&want) {
|
|
||||||
found_block = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
found_block
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn test_runtime_preset(
|
|
||||||
runtime: &'static str,
|
|
||||||
block_time: u64,
|
|
||||||
maybe_preset: Option<PresetId>,
|
|
||||||
) {
|
|
||||||
pezsp_tracing::try_init_simple();
|
|
||||||
maybe_build_runtimes();
|
|
||||||
maybe_build_chain_spec_builder();
|
|
||||||
maybe_build_omni_node();
|
|
||||||
|
|
||||||
let chain_spec_builder =
|
|
||||||
find_release_binary(&CHAIN_SPEC_BUILDER).expect("we built it above; qed");
|
|
||||||
let omni_node = find_release_binary(OMNI_NODE).expect("we built it above; qed");
|
|
||||||
let runtime_path = find_wasm(runtime).expect("we built it above; qed");
|
|
||||||
|
|
||||||
let random_seed: u32 = rand::thread_rng().gen();
|
|
||||||
let chain_spec_file = std::env::current_dir()
|
|
||||||
.unwrap()
|
|
||||||
.join(format!("{}_{}_{}.json", runtime, block_time, random_seed));
|
|
||||||
|
|
||||||
Command::new(chain_spec_builder)
|
|
||||||
.args(["-c", chain_spec_file.to_str().unwrap()])
|
|
||||||
.arg("create")
|
|
||||||
.args(["--relay-chain", "dontcare"])
|
|
||||||
.args(["-r", runtime_path.to_str().unwrap()])
|
|
||||||
.args(match maybe_preset {
|
|
||||||
Some(preset) => vec!["named-preset".to_string(), preset.to_string()],
|
|
||||||
None => vec!["default".to_string()],
|
|
||||||
})
|
|
||||||
.assert()
|
|
||||||
.success();
|
|
||||||
|
|
||||||
let mut child = Command::new(omni_node)
|
|
||||||
.arg("--tmp")
|
|
||||||
.args(["--chain", chain_spec_file.to_str().unwrap()])
|
|
||||||
.args(["--dev-block-time", block_time.to_string().as_str()])
|
|
||||||
.stderr(Stdio::piped())
|
|
||||||
.spawn()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Take stderr and parse it with timeout.
|
|
||||||
let stderr = child.stderr.take().unwrap();
|
|
||||||
let expected_blocks = (10_000 / block_time).saturating_div(2);
|
|
||||||
assert!(expected_blocks > 0, "test configuration is bad, should give it more time");
|
|
||||||
assert_eq!(imported_block_found(stderr, expected_blocks, 100).await, true);
|
|
||||||
std::fs::remove_file(chain_spec_file).unwrap();
|
|
||||||
child.kill().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets up omni-node to run a text exercise based on a chain spec.
|
|
||||||
async fn omni_node_test_setup(chain_spec_path: PathBuf) {
|
|
||||||
maybe_build_omni_node();
|
|
||||||
let omni_node = find_release_binary(OMNI_NODE).unwrap();
|
|
||||||
|
|
||||||
let mut child = Command::new(omni_node)
|
|
||||||
.arg("--dev")
|
|
||||||
.args(["--chain", chain_spec_path.to_str().unwrap()])
|
|
||||||
.stderr(Stdio::piped())
|
|
||||||
.spawn()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let stderr = child.stderr.take().unwrap();
|
|
||||||
assert_eq!(imported_block_found(stderr, 7, 100).await, true);
|
|
||||||
child.kill().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn works_with_different_block_times() {
|
|
||||||
test_runtime_preset(PARA_RUNTIME, 100, Some(DEV_RUNTIME_PRESET.into())).await;
|
|
||||||
test_runtime_preset(PARA_RUNTIME, 3000, Some(DEV_RUNTIME_PRESET.into())).await;
|
|
||||||
|
|
||||||
// we need this snippet just for docs
|
|
||||||
#[docify::export_content(csb)]
|
|
||||||
fn build_teyrchain_spec_works() {
|
|
||||||
let chain_spec_builder = find_release_binary(&CHAIN_SPEC_BUILDER).unwrap();
|
|
||||||
let runtime_path = find_wasm(PARA_RUNTIME).unwrap();
|
|
||||||
let output = "/tmp/demo-chain-spec.json";
|
|
||||||
let runtime_str = runtime_path.to_str().unwrap();
|
|
||||||
run_cmd!(
|
|
||||||
$chain_spec_builder -c $output create --relay-chain dontcare -r $runtime_str named-preset development
|
|
||||||
).expect("Failed to run command");
|
|
||||||
std::fs::remove_file(output).unwrap();
|
|
||||||
}
|
|
||||||
build_teyrchain_spec_works();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn teyrchain_runtime_works() {
|
|
||||||
// TODO: None doesn't work. But maybe it should? it would be misleading as many users might
|
|
||||||
// use it.
|
|
||||||
for preset in [Some(DEV_RUNTIME_PRESET.into()), Some(LOCAL_TESTNET_RUNTIME_PRESET.into())] {
|
|
||||||
test_runtime_preset(PARA_RUNTIME, 1000, preset).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn omni_node_dev_mode_works() {
|
|
||||||
//Omni Node in dev mode works with teyrchain's template `dev_chain_spec`
|
|
||||||
let dev_chain_spec = std::env::current_dir()
|
|
||||||
.unwrap()
|
|
||||||
.parent()
|
|
||||||
.unwrap()
|
|
||||||
.parent()
|
|
||||||
.unwrap()
|
|
||||||
.join("templates")
|
|
||||||
.join("teyrchain")
|
|
||||||
.join("dev_chain_spec.json");
|
|
||||||
omni_node_test_setup(dev_chain_spec).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
// This is a regresion test so that we still remain compatible with runtimes that use
|
|
||||||
// `para-id` in chain specs, instead of implementing the
|
|
||||||
// `pezcumulus_primitives_core::GetTeyrchainInfo`.
|
|
||||||
async fn omni_node_dev_mode_works_without_getteyrchaininfo() {
|
|
||||||
let dev_chain_spec = std::env::current_dir()
|
|
||||||
.unwrap()
|
|
||||||
.join("src/guides/teyrchain_without_getteyrchaininfo.json");
|
|
||||||
omni_node_test_setup(dev_chain_spec).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,798 +0,0 @@
|
|||||||
//! # Currency Pezpallet
|
|
||||||
//!
|
|
||||||
//! By the end of this guide, you will have written a small FRAME pezpallet (see
|
|
||||||
//! [`crate::pezkuwi_sdk::frame_runtime`]) that is capable of handling a simple crypto-currency.
|
|
||||||
//! This pezpallet will:
|
|
||||||
//!
|
|
||||||
//! 1. Allow anyone to mint new tokens into accounts (which is obviously not a great idea for a real
|
|
||||||
//! system).
|
|
||||||
//! 2. Allow any user that owns tokens to transfer them to others.
|
|
||||||
//! 3. Track the total issuance of all tokens at all times.
|
|
||||||
//!
|
|
||||||
//! > This guide will build a currency pezpallet from scratch using only the lowest primitives of
|
|
||||||
//! > FRAME, and is mainly intended for education, not *applicability*. For example, almost all
|
|
||||||
//! > FRAME-based runtimes use various techniques to re-use a currency pezpallet instead of writing
|
|
||||||
//! > one. Further advanced FRAME related topics are discussed in [`crate::reference_docs`].
|
|
||||||
//!
|
|
||||||
//! ## Writing Your First Pezpallet
|
|
||||||
//!
|
|
||||||
//! To get started, clone one of the templates mentioned in [`crate::pezkuwi_sdk::templates`]. We
|
|
||||||
//! recommend using the `pezkuwi-sdk-minimal-template`. You might need to change small parts of
|
|
||||||
//! this guide, namely the crate/package names, based on which template you use.
|
|
||||||
//!
|
|
||||||
//! > Be aware that you can read the entire source code backing this tutorial by clicking on the
|
|
||||||
//! > `source` button at the top right of the page.
|
|
||||||
//!
|
|
||||||
//! You should have studied the following modules as a prelude to this guide:
|
|
||||||
//!
|
|
||||||
//! - [`crate::reference_docs::blockchain_state_machines`]
|
|
||||||
//! - [`crate::reference_docs::trait_based_programming`]
|
|
||||||
//! - [`crate::pezkuwi_sdk::frame_runtime`]
|
|
||||||
//!
|
|
||||||
//! ## Topics Covered
|
|
||||||
//!
|
|
||||||
//! The following FRAME topics are covered in this guide:
|
|
||||||
//!
|
|
||||||
//! - [`pezpallet::storage`]
|
|
||||||
//! - [`pezpallet::call`]
|
|
||||||
//! - [`pezpallet::event`]
|
|
||||||
//! - [`pezpallet::error`]
|
|
||||||
//! - Basics of testing a pezpallet
|
|
||||||
//! - [Constructing a runtime](pezframe::runtime::prelude::construct_runtime)
|
|
||||||
//!
|
|
||||||
//! ### Shell Pezpallet
|
|
||||||
//!
|
|
||||||
//! Consider the following as a "shell pezpallet". We continue building the rest of this pezpallet
|
|
||||||
//! based on this template.
|
|
||||||
//!
|
|
||||||
//! [`pezpallet::config`] and [`pezpallet::pezpallet`] are both mandatory parts of any
|
|
||||||
//! pezpallet. Refer to the documentation of each to get an overview of what they do.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", shell_pallet)]
|
|
||||||
//!
|
|
||||||
//! All of the code that follows in this guide should live inside of the `mod pezpallet`.
|
|
||||||
//!
|
|
||||||
//! ### Storage
|
|
||||||
//!
|
|
||||||
//! First, we will need to create two onchain storage declarations.
|
|
||||||
//!
|
|
||||||
//! One should be a mapping from account-ids to a balance type, and one value that is the total
|
|
||||||
//! issuance.
|
|
||||||
//!
|
|
||||||
//! > For the rest of this guide, we will opt for a balance type of `u128`. For the sake of
|
|
||||||
//! > simplicity, we are hardcoding this type. In a real pezpallet is best practice to define it as
|
|
||||||
//! > a
|
|
||||||
//! > generic bounded type in the `Config` trait, and then specify it in the implementation.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", Balance)]
|
|
||||||
//!
|
|
||||||
//! The definition of these two storage items, based on [`pezpallet::storage`] details, is as
|
|
||||||
//! follows:
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", TotalIssuance)]
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", Balances)]
|
|
||||||
//!
|
|
||||||
//! ### Dispatchables
|
|
||||||
//!
|
|
||||||
//! Next, we will define the dispatchable functions. As per [`pezpallet::call`], these will be
|
|
||||||
//! defined as normal `fn`s attached to `struct Pezpallet`.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", impl_pallet)]
|
|
||||||
//!
|
|
||||||
//! The logic of these functions is self-explanatory. Instead, we will focus on the FRAME-related
|
|
||||||
//! details:
|
|
||||||
//!
|
|
||||||
//! - Where do `T::AccountId` and `T::RuntimeOrigin` come from? These are both defined in
|
|
||||||
//! [`pezframe::prelude::pezframe_system::Config`], therefore we can access them in `T`.
|
|
||||||
//! - What is `ensure_signed`, and what does it do with the aforementioned `T::RuntimeOrigin`? This
|
|
||||||
//! is outside the scope of this guide, and you can learn more about it in the origin reference
|
|
||||||
//! document ([`crate::reference_docs::frame_origin`]). For now, you should only know the
|
|
||||||
//! signature of the function: it takes a generic `T::RuntimeOrigin` and returns a
|
|
||||||
//! `Result<T::AccountId, _>`. So by the end of this function call, we know that this dispatchable
|
|
||||||
//! was signed by `sender`.
|
|
||||||
#![doc = docify::embed!("../../bizinikiwi/pezframe/system/src/lib.rs", ensure_signed)]
|
|
||||||
//!
|
|
||||||
//! - Where does `mutate`, `get` and `insert` and other storage APIs come from? All of them are
|
|
||||||
//! explained in the corresponding `type`, for example, for `Balances::<T>::insert`, you can look
|
|
||||||
//! into [`pezframe::prelude::StorageMap::insert`].
|
|
||||||
//!
|
|
||||||
//! - The return type of all dispatchable functions is [`pezframe::prelude::DispatchResult`]:
|
|
||||||
#![doc = docify::embed!("../../bizinikiwi/pezframe/support/src/dispatch.rs", DispatchResult)]
|
|
||||||
//!
|
|
||||||
//! Which is more or less a normal Rust `Result`, with a custom [`pezframe::prelude::DispatchError`] as
|
|
||||||
//! the `Err` variant. We won't cover this error in detail here, but importantly you should know
|
|
||||||
//! that there is an `impl From<&'static string> for DispatchError` provided (see
|
|
||||||
//! [here](`pezframe::prelude::DispatchError#impl-From<%26str>-for-DispatchError`)). Therefore,
|
|
||||||
//! we can use basic string literals as our error type and `.into()` them into `DispatchError`.
|
|
||||||
//!
|
|
||||||
//! - Why are all `get` and `mutate` functions returning an `Option`? This is the default behavior
|
|
||||||
//! of FRAME storage APIs. You can learn more about how to override this by looking into
|
|
||||||
//! [`pezpallet::storage`], and [`pezframe::prelude::ValueQuery`]/[`pezframe::prelude::OptionQuery`]
|
|
||||||
//!
|
|
||||||
//! ### Improving Errors
|
|
||||||
//!
|
|
||||||
//! How we handle error in the above snippets is fairly rudimentary. Let's look at how this can be
|
|
||||||
//! improved. First, we can use [`pezframe::prelude::ensure`] to express the error slightly better.
|
|
||||||
//! This macro will call `.into()` under the hood.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", transfer_better)]
|
|
||||||
//!
|
|
||||||
//! Moreover, you will learn in the [Defensive Programming
|
|
||||||
//! section](crate::reference_docs::defensive_programming) that it is always recommended to use
|
|
||||||
//! safe arithmetic operations in your runtime. By using [`pezframe::traits::CheckedSub`], we can not
|
|
||||||
//! only take a step in that direction, but also improve the error handing and make it slightly more
|
|
||||||
//! ergonomic.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", transfer_better_checked)]
|
|
||||||
//!
|
|
||||||
//! This is more or less all the logic that there is in this basic currency pezpallet!
|
|
||||||
//!
|
|
||||||
//! ### Your First (Test) Runtime
|
|
||||||
//!
|
|
||||||
//! The typical testing code of a pezpallet lives in a module that imports some preludes useful for
|
|
||||||
//! testing, similar to:
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! pub mod pezpallet {
|
|
||||||
//! // snip -- actually pezpallet code.
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! #[cfg(test)]
|
|
||||||
//! mod tests {
|
|
||||||
//! // bring in the testing prelude of frame
|
|
||||||
//! use pezframe::testing_prelude::*;
|
|
||||||
//! // bring in all pezpallet items
|
|
||||||
//! use super::pezpallet::*;
|
|
||||||
//!
|
|
||||||
//! // snip -- rest of the testing code.
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! Next, we create a "test runtime" in order to test our pezpallet. Recall from
|
|
||||||
//! [`crate::pezkuwi_sdk::frame_runtime`] that a runtime is a collection of pallets, expressed
|
|
||||||
//! through [`pezframe::runtime::prelude::construct_runtime`]. All runtimes also have to include
|
|
||||||
//! [`pezframe::prelude::pezframe_system`]. So we expect to see a runtime with two pezpallet,
|
|
||||||
//! `pezframe_system` and the one we just wrote.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", runtime)]
|
|
||||||
//!
|
|
||||||
//! > [`pezframe::pezpallet_macros::derive_impl`] is a FRAME feature that enables developers to have
|
|
||||||
//! > defaults for associated types.
|
|
||||||
//!
|
|
||||||
//! Recall that within our pezpallet, (almost) all blocks of code are generic over `<T: Config>`.
|
|
||||||
//! And, because `trait Config: pezframe_system::Config`, we can get access to all items in `Config`
|
|
||||||
//! (or `pezframe_system::Config`) using `T::NameOfItem`. This is all within the boundaries of how
|
|
||||||
//! Rust traits and generics work. If unfamiliar with this pattern, read
|
|
||||||
//! [`crate::reference_docs::trait_based_programming`] before going further.
|
|
||||||
//!
|
|
||||||
//! Crucially, a typical FRAME runtime contains a `struct Runtime`. The main role of this `struct`
|
|
||||||
//! is to implement the `trait Config` of all pallets. That is, anywhere within your pezpallet code
|
|
||||||
//! where you see `<T: Config>` (read: *"some type `T` that implements `Config`"*), in the runtime,
|
|
||||||
//! it can be replaced with `<Runtime>`, because `Runtime` implements `Config` of all pallets, as we
|
|
||||||
//! see above.
|
|
||||||
//!
|
|
||||||
//! Another way to think about this is that within a pezpallet, a lot of types are "unknown" and, we
|
|
||||||
//! only know that they will be provided at some later point. For example, when you write
|
|
||||||
//! `T::AccountId` (which is short for `<T as pezframe_system::Config>::AccountId`) in your
|
|
||||||
//! pezpallet, you are in fact saying "*Some type `AccountId` that will be known later*". That
|
|
||||||
//! "later" is in fact when you specify these types when you implement all `Config` traits for
|
|
||||||
//! `Runtime`.
|
|
||||||
//!
|
|
||||||
//! As you see above, `pezframe_system::Config` is setting the `AccountId` to `u64`. Of course, a
|
|
||||||
//! real runtime will not use this type, and instead reside to a proper type like a 32-byte standard
|
|
||||||
//! public key. This is a HUGE benefit that FRAME developers can tap into: through the framework
|
|
||||||
//! being so generic, different types can always be customized to simple things when needed.
|
|
||||||
//!
|
|
||||||
//! > Imagine how hard it would have been if all tests had to use a real 32-byte account id, as
|
|
||||||
//! > opposed to just a u64 number 🙈.
|
|
||||||
//!
|
|
||||||
//! ### Your First Test
|
|
||||||
//!
|
|
||||||
//! The above is all you need to execute the dispatchables of your pezpallet. The last thing you
|
|
||||||
//! need to learn is that all of your pezpallet testing code should be wrapped in
|
|
||||||
//! [`pezframe::testing_prelude::TestState`]. This is a type that provides access to an in-memory state
|
|
||||||
//! to be used in our tests.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", first_test)]
|
|
||||||
//!
|
|
||||||
//! In the first test, we simply assert that there is no total issuance, and no balance associated
|
|
||||||
//! with Alice's account. Then, we mint some balance into Alice's, and re-check.
|
|
||||||
//!
|
|
||||||
//! As noted above, the `T::AccountId` is now `u64`. Moreover, `Runtime` is replacing `<T: Config>`.
|
|
||||||
//! This is why for example you see `Balances::<Runtime>::get(..)`. Finally, notice that the
|
|
||||||
//! dispatchables are simply functions that can be called on top of the `Pezpallet` struct.
|
|
||||||
//!
|
|
||||||
//! Congratulations! You have written your first pezpallet and tested it! Next, we learn a few
|
|
||||||
//! optional steps to improve our pezpallet.
|
|
||||||
//!
|
|
||||||
//! ## Improving the Currency Pezpallet
|
|
||||||
//!
|
|
||||||
//! ### Better Test Setup
|
|
||||||
//!
|
|
||||||
//! Idiomatic FRAME pallets often use Builder pattern to define their initial state.
|
|
||||||
//!
|
|
||||||
//! > The Pezkuwi Blockchain Academy's Rust entrance exam has a
|
|
||||||
//! > [section](https://github.com/pezkuwichain/kurdistan_blockchain-akademy/blob/main/src/m_builder.rs)
|
|
||||||
//! > on this that you can use to learn the Builder Pattern.
|
|
||||||
//!
|
|
||||||
//! Let's see how we can implement a better test setup using this pattern. First, we define a
|
|
||||||
//! `struct StateBuilder`.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", StateBuilder)]
|
|
||||||
//!
|
|
||||||
//! This struct is meant to contain the same list of accounts and balances that we want to have at
|
|
||||||
//! the beginning of each block. We hardcoded this to `let accounts = vec![(ALICE, 100), (2, 100)];`
|
|
||||||
//! so far. Then, if desired, we attach a default value for this struct.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", default_state_builder)]
|
|
||||||
//!
|
|
||||||
//! Like any other builder pattern, we attach functions to the type to mutate its internal
|
|
||||||
//! properties.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", impl_state_builder_add)]
|
|
||||||
//!
|
|
||||||
//! Finally --the useful part-- we write our own custom `build_and_execute` function on
|
|
||||||
//! this type. This function will do multiple things:
|
|
||||||
//!
|
|
||||||
//! 1. It would consume `self` to produce our `TestState` based on the properties that we attached
|
|
||||||
//! to `self`.
|
|
||||||
//! 2. It would execute any test function that we pass in as closure.
|
|
||||||
//! 3. A nifty trick, this allows our test setup to have some code that is executed both before and
|
|
||||||
//! after each test. For example, in this test, we do some additional checking about the
|
|
||||||
//! correctness of the `TotalIssuance`. We leave it up to you as an exercise to learn why the
|
|
||||||
//! assertion should always hold, and how it is checked.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", impl_state_builder_build)]
|
|
||||||
//!
|
|
||||||
//! We can write tests that specifically check the initial state, and making sure our `StateBuilder`
|
|
||||||
//! is working exactly as intended.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", state_builder_works)]
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", state_builder_add_balance)]
|
|
||||||
//!
|
|
||||||
//! ### More Tests
|
|
||||||
//!
|
|
||||||
//! Now that we have a more ergonomic test setup, let's see how a well written test for transfer and
|
|
||||||
//! mint would look like.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", transfer_works)]
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", mint_works)]
|
|
||||||
//!
|
|
||||||
//! It is always a good idea to build a mental model where you write *at least* one test for each
|
|
||||||
//! "success path" of a dispatchable, and one test for each "failure path", such as:
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", transfer_from_non_existent_fails)]
|
|
||||||
//!
|
|
||||||
//! We leave it up to you to write a test that triggers the `InsufficientBalance` error.
|
|
||||||
//!
|
|
||||||
//! ### Event and Error
|
|
||||||
//!
|
|
||||||
//! Our pezpallet is mainly missing two parts that are common in most FRAME pallets: Events, and
|
|
||||||
//! Errors. First, let's understand what each is.
|
|
||||||
//!
|
|
||||||
//! - **Error**: The static string-based error scheme we used so far is good for readability, but it
|
|
||||||
//! has a few drawbacks. The biggest problem with strings are that they are not type safe, e.g. a
|
|
||||||
//! match statement cannot be exhaustive. These string literals will bloat the final wasm blob,
|
|
||||||
//! and are relatively heavy to transmit and encode/decode. Moreover, it is easy to mistype them
|
|
||||||
//! by one character. FRAME errors are exactly a solution to maintain readability, whilst fixing
|
|
||||||
//! the drawbacks mentioned. In short, we use an enum to represent different variants of our
|
|
||||||
//! error. These variants are then mapped in an efficient way (using only `u8` indices) to
|
|
||||||
//! [`pezsp_runtime::DispatchError::Module`]. Read more about this in [`pezpallet::error`].
|
|
||||||
//!
|
|
||||||
//! - **Event**: Events are akin to the return type of dispatchables. They are mostly data blobs
|
|
||||||
//! emitted by the runtime to let outside world know what is happening inside the pezpallet. Since
|
|
||||||
//! otherwise, the outside world does not have an easy access to the state changes. They should
|
|
||||||
//! represent what happened at the end of a dispatch operation. Therefore, the convention is to
|
|
||||||
//! use passive tense for event names (eg. `SomethingHappened`). This allows other sub-systems or
|
|
||||||
//! external parties (eg. a light-node, a DApp) to listen to particular events happening, without
|
|
||||||
//! needing to re-execute the whole state transition function.
|
|
||||||
//!
|
|
||||||
//! With the explanation out of the way, let's see how these components can be added. Both follow a
|
|
||||||
//! fairly familiar syntax: normal Rust enums, with extra [`pezpallet::event`] and
|
|
||||||
//! [`pezpallet::error`] attributes attached.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", Event)]
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", Error)]
|
|
||||||
//!
|
|
||||||
//! One slightly custom part of this is the [`pezpallet::generate_deposit`] part. Without going into
|
|
||||||
//! too much detail, in order for a pezpallet to emit events to the rest of the system, it needs to
|
|
||||||
//! do two things:
|
|
||||||
//!
|
|
||||||
//! 1. Declare a type in its `Config` that refers to the overarching event type of the runtime. In
|
|
||||||
//! short, by doing this, the pezpallet is expressing an important bound: `type RuntimeEvent:
|
|
||||||
//! From<Event<Self>>`. Read: a `RuntimeEvent` exists, and it can be created from the local `enum
|
|
||||||
//! Event` of this pezpallet. This enables the pezpallet to convert its `Event` into `RuntimeEvent`,
|
|
||||||
//! and store it where needed.
|
|
||||||
//!
|
|
||||||
//! 2. But, doing this conversion and storing is too much to expect each pezpallet to define. FRAME
|
|
||||||
//! provides a default way of storing events, and this is what [`pezpallet::generate_deposit`] is
|
|
||||||
//! doing.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", config_v2)]
|
|
||||||
//!
|
|
||||||
//! > These `Runtime*` types are better explained in
|
|
||||||
//! > [`crate::reference_docs::frame_runtime_types`].
|
|
||||||
//!
|
|
||||||
//! Then, we can rewrite the `transfer` dispatchable as such:
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", transfer_v2)]
|
|
||||||
//!
|
|
||||||
//! Then, notice how now we would need to provide this `type RuntimeEvent` in our test runtime
|
|
||||||
//! setup.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-pezpallet/src/lib.rs", runtime_v2)]
|
|
||||||
//!
|
|
||||||
//! In this snippet, the actual `RuntimeEvent` type (right hand side of `type RuntimeEvent =
|
|
||||||
//! RuntimeEvent`) is generated by
|
|
||||||
//! [`construct_runtime`](pezframe::runtime::prelude::construct_runtime). An interesting way to inspect
|
|
||||||
//! this type is to see its definition in rust-docs:
|
|
||||||
//! [`crate::guides::your_first_pallet::pezpallet_v2::tests::runtime_v2::RuntimeEvent`].
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//! ## What Next?
|
|
||||||
//!
|
|
||||||
//! The following topics where used in this guide, but not covered in depth. It is suggested to
|
|
||||||
//! study them subsequently:
|
|
||||||
//!
|
|
||||||
//! - [`crate::reference_docs::defensive_programming`].
|
|
||||||
//! - [`crate::reference_docs::frame_origin`].
|
|
||||||
//! - [`crate::reference_docs::frame_runtime_types`].
|
|
||||||
//! - The pezpallet we wrote in this guide was using `dev_mode`, learn more in
|
|
||||||
//! [`pezpallet::config`].
|
|
||||||
//! - Learn more about the individual pezpallet items/macros, such as event and errors and call, in
|
|
||||||
//! [`pezframe::pezpallet_macros`].
|
|
||||||
//!
|
|
||||||
//! [`pezpallet::storage`]: pezframe_support::pezpallet_macros::storage
|
|
||||||
//! [`pezpallet::call`]: pezframe_support::pezpallet_macros::call
|
|
||||||
//! [`pezpallet::event`]: pezframe_support::pezpallet_macros::event
|
|
||||||
//! [`pezpallet::error`]: pezframe_support::pezpallet_macros::error
|
|
||||||
//! [`pezpallet::pezpallet`]: pezframe_support::pezpallet
|
|
||||||
//! [`pezpallet::config`]: pezframe_support::pezpallet_macros::config
|
|
||||||
//! [`pezpallet::generate_deposit`]: pezframe_support::pezpallet_macros::generate_deposit
|
|
||||||
//! [`frame`]: crate::pezkuwi_sdk::frame_runtime
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[pezframe::pezpallet(dev_mode)]
|
|
||||||
pub mod shell_pallet {
|
|
||||||
use pezframe::prelude::*;
|
|
||||||
|
|
||||||
#[pezpallet::config]
|
|
||||||
pub trait Config: pezframe_system::Config {}
|
|
||||||
|
|
||||||
#[pezpallet::pezpallet]
|
|
||||||
pub struct Pezpallet<T>(_);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pezframe::pezpallet(dev_mode)]
|
|
||||||
pub mod pezpallet {
|
|
||||||
use pezframe::prelude::*;
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
pub type Balance = u128;
|
|
||||||
|
|
||||||
#[pezpallet::config]
|
|
||||||
pub trait Config: pezframe_system::Config {}
|
|
||||||
|
|
||||||
#[pezpallet::pezpallet]
|
|
||||||
pub struct Pezpallet<T>(_);
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
/// Single storage item, of type `Balance`.
|
|
||||||
#[pezpallet::storage]
|
|
||||||
pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
/// A mapping from `T::AccountId` to `Balance`
|
|
||||||
#[pezpallet::storage]
|
|
||||||
pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
|
|
||||||
|
|
||||||
#[docify::export(impl_pallet)]
|
|
||||||
#[pezpallet::call]
|
|
||||||
impl<T: Config> Pezpallet<T> {
|
|
||||||
/// An unsafe mint that can be called by anyone. Not a great idea.
|
|
||||||
pub fn mint_unsafe(
|
|
||||||
origin: T::RuntimeOrigin,
|
|
||||||
dest: T::AccountId,
|
|
||||||
amount: Balance,
|
|
||||||
) -> DispatchResult {
|
|
||||||
// ensure that this is a signed account, but we don't really check `_anyone`.
|
|
||||||
let _anyone = ensure_signed(origin)?;
|
|
||||||
|
|
||||||
// update the balances map. Notice how all `<T: Config>` remains as `<T>`.
|
|
||||||
Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
|
|
||||||
// update total issuance.
|
|
||||||
TotalIssuance::<T>::mutate(|t| *t = Some(t.unwrap_or(0) + amount));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transfer `amount` from `origin` to `dest`.
|
|
||||||
pub fn transfer(
|
|
||||||
origin: T::RuntimeOrigin,
|
|
||||||
dest: T::AccountId,
|
|
||||||
amount: Balance,
|
|
||||||
) -> DispatchResult {
|
|
||||||
let sender = ensure_signed(origin)?;
|
|
||||||
|
|
||||||
// ensure sender has enough balance, and if so, calculate what is left after `amount`.
|
|
||||||
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
|
|
||||||
if sender_balance < amount {
|
|
||||||
return Err("InsufficientBalance".into());
|
|
||||||
}
|
|
||||||
let remainder = sender_balance - amount;
|
|
||||||
|
|
||||||
// update sender and dest balances.
|
|
||||||
Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
|
|
||||||
Balances::<T>::insert(&sender, remainder);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
impl<T: Config> Pezpallet<T> {
|
|
||||||
#[docify::export]
|
|
||||||
pub fn transfer_better(
|
|
||||||
origin: T::RuntimeOrigin,
|
|
||||||
dest: T::AccountId,
|
|
||||||
amount: Balance,
|
|
||||||
) -> DispatchResult {
|
|
||||||
let sender = ensure_signed(origin)?;
|
|
||||||
|
|
||||||
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
|
|
||||||
ensure!(sender_balance >= amount, "InsufficientBalance");
|
|
||||||
let remainder = sender_balance - amount;
|
|
||||||
|
|
||||||
// .. snip
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
/// Transfer `amount` from `origin` to `dest`.
|
|
||||||
pub fn transfer_better_checked(
|
|
||||||
origin: T::RuntimeOrigin,
|
|
||||||
dest: T::AccountId,
|
|
||||||
amount: Balance,
|
|
||||||
) -> DispatchResult {
|
|
||||||
let sender = ensure_signed(origin)?;
|
|
||||||
|
|
||||||
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
|
|
||||||
let remainder = sender_balance.checked_sub(amount).ok_or("InsufficientBalance")?;
|
|
||||||
|
|
||||||
// .. snip
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(test, doc))]
|
|
||||||
pub(crate) mod tests {
|
|
||||||
use crate::guides::your_first_pallet::pezpallet::*;
|
|
||||||
|
|
||||||
#[docify::export(testing_prelude)]
|
|
||||||
use pezframe::testing_prelude::*;
|
|
||||||
|
|
||||||
pub(crate) const ALICE: u64 = 1;
|
|
||||||
pub(crate) const BOB: u64 = 2;
|
|
||||||
pub(crate) const CHARLIE: u64 = 3;
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
// This runtime is only used for testing, so it should be somewhere like `#[cfg(test)] mod
|
|
||||||
// tests { .. }`
|
|
||||||
mod runtime {
|
|
||||||
use super::*;
|
|
||||||
// we need to reference our `mod pezpallet` as an identifier to pass to
|
|
||||||
// `construct_runtime`.
|
|
||||||
// YOU HAVE TO CHANGE THIS LINE BASED ON YOUR TEMPLATE
|
|
||||||
use crate::guides::your_first_pallet::pezpallet as pezpallet_currency;
|
|
||||||
|
|
||||||
construct_runtime!(
|
|
||||||
pub enum Runtime {
|
|
||||||
// ---^^^^^^ This is where `enum Runtime` is defined.
|
|
||||||
System: pezframe_system,
|
|
||||||
Currency: pezpallet_currency,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
|
|
||||||
impl pezframe_system::Config for Runtime {
|
|
||||||
type Block = MockBlock<Runtime>;
|
|
||||||
// within pezpallet we just said `<T as pezframe_system::Config>::AccountId`, now we
|
|
||||||
// finally specified it.
|
|
||||||
type AccountId = u64;
|
|
||||||
}
|
|
||||||
|
|
||||||
// our simple pezpallet has nothing to be configured.
|
|
||||||
impl pezpallet_currency::Config for Runtime {}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) use runtime::*;
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
#[docify::export]
|
|
||||||
fn new_test_state_basic() -> TestState {
|
|
||||||
let mut state = TestState::new_empty();
|
|
||||||
let accounts = vec![(ALICE, 100), (BOB, 100)];
|
|
||||||
state.execute_with(|| {
|
|
||||||
for (who, amount) in &accounts {
|
|
||||||
Balances::<Runtime>::insert(who, amount);
|
|
||||||
TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
state
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
pub(crate) struct StateBuilder {
|
|
||||||
balances: Vec<(<Runtime as pezframe_system::Config>::AccountId, Balance)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export(default_state_builder)]
|
|
||||||
impl Default for StateBuilder {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self { balances: vec![(ALICE, 100), (BOB, 100)] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export(impl_state_builder_add)]
|
|
||||||
impl StateBuilder {
|
|
||||||
fn add_balance(
|
|
||||||
mut self,
|
|
||||||
who: <Runtime as pezframe_system::Config>::AccountId,
|
|
||||||
amount: Balance,
|
|
||||||
) -> Self {
|
|
||||||
self.balances.push((who, amount));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export(impl_state_builder_build)]
|
|
||||||
impl StateBuilder {
|
|
||||||
pub(crate) fn build_and_execute(self, test: impl FnOnce() -> ()) {
|
|
||||||
let mut ext = TestState::new_empty();
|
|
||||||
ext.execute_with(|| {
|
|
||||||
for (who, amount) in &self.balances {
|
|
||||||
Balances::<Runtime>::insert(who, amount);
|
|
||||||
TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ext.execute_with(test);
|
|
||||||
|
|
||||||
// assertions that must always hold
|
|
||||||
ext.execute_with(|| {
|
|
||||||
assert_eq!(
|
|
||||||
Balances::<Runtime>::iter().map(|(_, x)| x).sum::<u128>(),
|
|
||||||
TotalIssuance::<Runtime>::get().unwrap_or_default()
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[test]
|
|
||||||
fn first_test() {
|
|
||||||
TestState::new_empty().execute_with(|| {
|
|
||||||
// We expect Alice's account to have no funds.
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&ALICE), None);
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), None);
|
|
||||||
|
|
||||||
// mint some funds into Alice's account.
|
|
||||||
assert_ok!(Pezpallet::<Runtime>::mint_unsafe(
|
|
||||||
RuntimeOrigin::signed(ALICE),
|
|
||||||
ALICE,
|
|
||||||
100
|
|
||||||
));
|
|
||||||
|
|
||||||
// re-check the above
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(100));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[test]
|
|
||||||
fn state_builder_works() {
|
|
||||||
StateBuilder::default().build_and_execute(|| {
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[test]
|
|
||||||
fn state_builder_add_balance() {
|
|
||||||
StateBuilder::default().add_balance(CHARLIE, 42).build_and_execute(|| {
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(42));
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn state_builder_duplicate_genesis_fails() {
|
|
||||||
StateBuilder::default()
|
|
||||||
.add_balance(CHARLIE, 42)
|
|
||||||
.add_balance(CHARLIE, 43)
|
|
||||||
.build_and_execute(|| {
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[test]
|
|
||||||
fn mint_works() {
|
|
||||||
StateBuilder::default().build_and_execute(|| {
|
|
||||||
// given the initial state, when:
|
|
||||||
assert_ok!(Pezpallet::<Runtime>::mint_unsafe(
|
|
||||||
RuntimeOrigin::signed(ALICE),
|
|
||||||
BOB,
|
|
||||||
100
|
|
||||||
));
|
|
||||||
|
|
||||||
// then:
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(200));
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(300));
|
|
||||||
|
|
||||||
// given:
|
|
||||||
assert_ok!(Pezpallet::<Runtime>::mint_unsafe(
|
|
||||||
RuntimeOrigin::signed(ALICE),
|
|
||||||
CHARLIE,
|
|
||||||
100
|
|
||||||
));
|
|
||||||
|
|
||||||
// then:
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(100));
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(400));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[test]
|
|
||||||
fn transfer_works() {
|
|
||||||
StateBuilder::default().build_and_execute(|| {
|
|
||||||
// given the initial state, when:
|
|
||||||
assert_ok!(Pezpallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
|
|
||||||
|
|
||||||
// then:
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
|
||||||
|
|
||||||
// when:
|
|
||||||
assert_ok!(Pezpallet::<Runtime>::transfer(RuntimeOrigin::signed(BOB), ALICE, 50));
|
|
||||||
|
|
||||||
// then:
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[test]
|
|
||||||
fn transfer_from_non_existent_fails() {
|
|
||||||
StateBuilder::default().build_and_execute(|| {
|
|
||||||
// given the initial state, when:
|
|
||||||
assert_err!(
|
|
||||||
Pezpallet::<Runtime>::transfer(RuntimeOrigin::signed(CHARLIE), ALICE, 10),
|
|
||||||
"NonExistentAccount"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then nothing has changed.
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pezframe::pezpallet(dev_mode)]
|
|
||||||
pub mod pezpallet_v2 {
|
|
||||||
use super::pezpallet::Balance;
|
|
||||||
use pezframe::prelude::*;
|
|
||||||
|
|
||||||
#[docify::export(config_v2)]
|
|
||||||
#[pezpallet::config]
|
|
||||||
pub trait Config: pezframe_system::Config {
|
|
||||||
/// The overarching event type of the runtime.
|
|
||||||
#[allow(deprecated)]
|
|
||||||
type RuntimeEvent: From<Event<Self>>
|
|
||||||
+ IsType<<Self as pezframe_system::Config>::RuntimeEvent>
|
|
||||||
+ TryInto<Event<Self>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pezpallet::pezpallet]
|
|
||||||
pub struct Pezpallet<T>(_);
|
|
||||||
|
|
||||||
#[pezpallet::storage]
|
|
||||||
pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
|
|
||||||
|
|
||||||
#[pezpallet::storage]
|
|
||||||
pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[pezpallet::error]
|
|
||||||
pub enum Error<T> {
|
|
||||||
/// Account does not exist.
|
|
||||||
NonExistentAccount,
|
|
||||||
/// Account does not have enough balance.
|
|
||||||
InsufficientBalance,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[pezpallet::event]
|
|
||||||
#[pezpallet::generate_deposit(pub(super) fn deposit_event)]
|
|
||||||
pub enum Event<T: Config> {
|
|
||||||
/// A transfer succeeded.
|
|
||||||
Transferred { from: T::AccountId, to: T::AccountId, amount: Balance },
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pezpallet::call]
|
|
||||||
impl<T: Config> Pezpallet<T> {
|
|
||||||
#[docify::export(transfer_v2)]
|
|
||||||
pub fn transfer(
|
|
||||||
origin: T::RuntimeOrigin,
|
|
||||||
dest: T::AccountId,
|
|
||||||
amount: Balance,
|
|
||||||
) -> DispatchResult {
|
|
||||||
let sender = ensure_signed(origin)?;
|
|
||||||
|
|
||||||
// ensure sender has enough balance, and if so, calculate what is left after `amount`.
|
|
||||||
let sender_balance =
|
|
||||||
Balances::<T>::get(&sender).ok_or(Error::<T>::NonExistentAccount)?;
|
|
||||||
let remainder =
|
|
||||||
sender_balance.checked_sub(amount).ok_or(Error::<T>::InsufficientBalance)?;
|
|
||||||
|
|
||||||
Balances::<T>::mutate(&dest, |b| *b = Some(b.unwrap_or(0) + amount));
|
|
||||||
Balances::<T>::insert(&sender, remainder);
|
|
||||||
|
|
||||||
Self::deposit_event(Event::<T>::Transferred { from: sender, to: dest, amount });
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(test, doc))]
|
|
||||||
pub mod tests {
|
|
||||||
use super::{super::pezpallet::tests::StateBuilder, *};
|
|
||||||
use pezframe::testing_prelude::*;
|
|
||||||
const ALICE: u64 = 1;
|
|
||||||
const BOB: u64 = 2;
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
pub mod runtime_v2 {
|
|
||||||
use super::*;
|
|
||||||
use crate::guides::your_first_pallet::pezpallet_v2 as pezpallet_currency;
|
|
||||||
|
|
||||||
construct_runtime!(
|
|
||||||
pub enum Runtime {
|
|
||||||
System: pezframe_system,
|
|
||||||
Currency: pezpallet_currency,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
|
|
||||||
impl pezframe_system::Config for Runtime {
|
|
||||||
type Block = MockBlock<Runtime>;
|
|
||||||
type AccountId = u64;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pezpallet_currency::Config for Runtime {
|
|
||||||
type RuntimeEvent = RuntimeEvent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) use runtime_v2::*;
|
|
||||||
|
|
||||||
#[docify::export(transfer_works_v2)]
|
|
||||||
#[test]
|
|
||||||
fn transfer_works() {
|
|
||||||
StateBuilder::default().build_and_execute(|| {
|
|
||||||
// skip the genesis block, as events are not deposited there and we need them for
|
|
||||||
// the final assertion.
|
|
||||||
System::set_block_number(ALICE);
|
|
||||||
|
|
||||||
// given the initial state, when:
|
|
||||||
assert_ok!(Pezpallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
|
|
||||||
|
|
||||||
// then:
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
|
|
||||||
assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
|
|
||||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
|
||||||
|
|
||||||
// now we can also check that an event has been deposited:
|
|
||||||
assert_eq!(
|
|
||||||
System::read_events_for_pallet::<Event<Runtime>>(),
|
|
||||||
vec![Event::Transferred { from: ALICE, to: BOB, amount: 50 }]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,188 +0,0 @@
|
|||||||
//! # Your first Runtime
|
|
||||||
//!
|
|
||||||
//! This guide will walk you through the steps to add your pezpallet to a runtime.
|
|
||||||
//!
|
|
||||||
//! The good news is, in [`crate::guides::your_first_pallet`], we have already created a _test_
|
|
||||||
//! runtime that was used for testing, and a real runtime is not that much different!
|
|
||||||
//!
|
|
||||||
//! ## Setup
|
|
||||||
//!
|
|
||||||
//! A runtime shares a few similar setup requirements as with a pezpallet:
|
|
||||||
//!
|
|
||||||
//! * importing [`frame`], [`codec`], and [`scale_info`] crates.
|
|
||||||
//! * following the [`std` feature-gating](crate::pezkuwi_sdk::bizinikiwi#wasm-build) pattern.
|
|
||||||
//!
|
|
||||||
//! But, more specifically, it also contains:
|
|
||||||
//!
|
|
||||||
//! * a `build.rs` that uses [`bizinikiwi_wasm_builder`]. This entails declaring
|
|
||||||
//! `[build-dependencies]` in the Cargo manifest file:
|
|
||||||
//!
|
|
||||||
//! ```ignore
|
|
||||||
//! [build-dependencies]
|
|
||||||
//! bizinikiwi-wasm-builder = { ... }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! >Note that a runtime must always be one-runtime-per-crate. You cannot define multiple runtimes
|
|
||||||
//! per rust crate.
|
|
||||||
//!
|
|
||||||
//! You can find the full code of this guide in [`first_runtime`].
|
|
||||||
//!
|
|
||||||
//! ## Your First Runtime
|
|
||||||
//!
|
|
||||||
//! The first new property of a real runtime that it must define its
|
|
||||||
//! [`pezframe::runtime::prelude::RuntimeVersion`]:
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", VERSION)]
|
|
||||||
//!
|
|
||||||
//! The version contains a number of very important fields, such as `spec_version` and `spec_name`
|
|
||||||
//! that play an important role in identifying your runtime and its version, more importantly in
|
|
||||||
//! runtime upgrades. More about runtime upgrades in
|
|
||||||
//! [`crate::reference_docs::frame_runtime_upgrades_and_migrations`].
|
|
||||||
//!
|
|
||||||
//! Then, a real runtime also contains the `impl` of all individual pallets' `trait Config` for
|
|
||||||
//! `struct Runtime`, and a [`pezframe::runtime::prelude::construct_runtime`] macro that amalgamates
|
|
||||||
//! them all.
|
|
||||||
//!
|
|
||||||
//! In the case of our example:
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", our_config_impl)]
|
|
||||||
//!
|
|
||||||
//! In this example, we bring in a number of other pallets from [`frame`] into the runtime, each of
|
|
||||||
//! their `Config` need to be implemented for `struct Runtime`:
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", config_impls)]
|
|
||||||
//!
|
|
||||||
//! Notice how we use [`pezframe::pezpallet_macros::derive_impl`] to provide "default" configuration
|
|
||||||
//! items for each pezpallet. Feel free to dive into the definition of each default prelude (eg.
|
|
||||||
//! [`pezframe::prelude::pezframe_system::pezpallet::config_preludes`]) to learn more which types are
|
|
||||||
//! exactly used.
|
|
||||||
//!
|
|
||||||
//! Recall that in test runtime in [`crate::guides::your_first_pallet`], we provided `type AccountId
|
|
||||||
//! = u64` to `pezframe_system`, while in this case we rely on whatever is provided by
|
|
||||||
//! [`SolochainDefaultConfig`], which is indeed a "real" 32 byte account id.
|
|
||||||
//!
|
|
||||||
//! Then, a familiar instance of `construct_runtime` amalgamates all of the pallets:
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", cr)]
|
|
||||||
//!
|
|
||||||
//! Recall from [`crate::reference_docs::wasm_meta_protocol`] that every (real) runtime needs to
|
|
||||||
//! implement a set of runtime APIs that will then let the node to communicate with it. The final
|
|
||||||
//! steps of crafting a runtime are related to achieving exactly this.
|
|
||||||
//!
|
|
||||||
//! First, we define a number of types that eventually lead to the creation of an instance of
|
|
||||||
//! [`pezframe::runtime::prelude::Executive`]. The executive is a handy FRAME utility that, through
|
|
||||||
//! amalgamating all pallets and further types, implements some of the very very core pieces of the
|
|
||||||
//! runtime logic, such as how blocks are executed and other runtime-api implementations.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", runtime_types)]
|
|
||||||
//!
|
|
||||||
//! Finally, we use [`pezframe::runtime::prelude::impl_runtime_apis`] to implement all of the runtime
|
|
||||||
//! APIs that the runtime wishes to expose. As you will see in the code, most of these runtime API
|
|
||||||
//! implementations are merely forwarding calls to `RuntimeExecutive` which handles the actual
|
|
||||||
//! logic. Given that the implementation block is somewhat large, we won't repeat it here. You can
|
|
||||||
//! look for `impl_runtime_apis!` in [`first_runtime`].
|
|
||||||
//!
|
|
||||||
//! ```ignore
|
|
||||||
//! impl_runtime_apis! {
|
|
||||||
//! impl apis::Core<Block> for Runtime {
|
|
||||||
//! fn version() -> RuntimeVersion {
|
|
||||||
//! VERSION
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn execute_block(block: Block) {
|
|
||||||
//! RuntimeExecutive::execute_block(block)
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn initialize_block(header: &Header) -> ExtrinsicInclusionMode {
|
|
||||||
//! RuntimeExecutive::initialize_block(header)
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // many more trait impls...
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! And that more or less covers the details of how you would write a real runtime!
|
|
||||||
//!
|
|
||||||
//! Once you compile a crate that contains a runtime as above, simply running `cargo build` will
|
|
||||||
//! generate the wasm blobs and place them under `./target/release/wbuild`, as explained
|
|
||||||
//! [here](crate::pezkuwi_sdk::bizinikiwi#wasm-build).
|
|
||||||
//!
|
|
||||||
//! ## Genesis Configuration
|
|
||||||
//!
|
|
||||||
//! Every runtime specifies a number of runtime APIs that help the outer world (most notably, a
|
|
||||||
//! `node`) know what is the genesis state of this runtime. These APIs are then used to generate
|
|
||||||
//! what is known as a **Chain Specification, or chain spec for short**. A chain spec is the
|
|
||||||
//! primary way to run a new chain.
|
|
||||||
//!
|
|
||||||
//! These APIs are defined in [`pezsp_genesis_builder`], and are re-exposed as a part of
|
|
||||||
//! [`pezframe::runtime::apis`]. Therefore, the implementation blocks can be found inside of
|
|
||||||
//! `impl_runtime_apis!` similar to:
|
|
||||||
//!
|
|
||||||
//! ```ignore
|
|
||||||
//! impl_runtime_apis! {
|
|
||||||
//! impl apis::GenesisBuilder<Block> for Runtime {
|
|
||||||
//! fn build_state(config: Vec<u8>) -> GenesisBuilderResult {
|
|
||||||
//! build_state::<RuntimeGenesisConfig>(config)
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn get_preset(id: &Option<PresetId>) -> Option<Vec<u8>> {
|
|
||||||
//! get_preset::<RuntimeGenesisConfig>(id, self::genesis_config_presets::get_preset)
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn preset_names() -> Vec<PresetId> {
|
|
||||||
//! crate::genesis_config_presets::preset_names()
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! The implementation of these function can naturally vary from one runtime to the other, but the
|
|
||||||
//! overall pattern is common. For the case of this runtime, we do the following:
|
|
||||||
//!
|
|
||||||
//! 1. Expose one non-default preset, namely [`pezsp_genesis_builder::DEV_RUNTIME_PRESET`]. This
|
|
||||||
//! means our runtime has two "presets" of genesis state in total: `DEV_RUNTIME_PRESET` and
|
|
||||||
//! `None`.
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", preset_names)]
|
|
||||||
//!
|
|
||||||
//! For `build_state` and `get_preset`, we use the helper functions provide by frame:
|
|
||||||
//!
|
|
||||||
//! * [`pezframe::runtime::prelude::build_state`] and [`pezframe::runtime::prelude::get_preset`].
|
|
||||||
//!
|
|
||||||
//! Indeed, our runtime needs to specify what its `DEV_RUNTIME_PRESET` genesis state should be like:
|
|
||||||
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", development_config_genesis)]
|
|
||||||
//!
|
|
||||||
//! For more in-depth information about `GenesisConfig`, `ChainSpec`, the `GenesisBuilder` API and
|
|
||||||
//! `chain-spec-builder`, see [`crate::reference_docs::chain_spec_genesis`].
|
|
||||||
//!
|
|
||||||
//! ## Next Step
|
|
||||||
//!
|
|
||||||
//! See [`crate::guides::your_first_node`].
|
|
||||||
//!
|
|
||||||
//! ## Further Reading
|
|
||||||
//!
|
|
||||||
//! 1. To learn more about signed extensions, see [`crate::reference_docs::signed_extensions`].
|
|
||||||
//! 2. `AllPalletsWithSystem` is also generated by `construct_runtime`, as explained in
|
|
||||||
//! [`crate::reference_docs::frame_runtime_types`].
|
|
||||||
//! 3. `Executive` supports more generics, most notably allowing the runtime to configure more
|
|
||||||
//! runtime migrations, as explained in
|
|
||||||
//! [`crate::reference_docs::frame_runtime_upgrades_and_migrations`].
|
|
||||||
//! 4. Learn more about adding and implementing runtime apis in
|
|
||||||
//! [`crate::reference_docs::custom_runtime_api_rpc`].
|
|
||||||
//! 5. To see a complete example of a runtime+pezpallet that is similar to this guide, please see
|
|
||||||
//! [`crate::pezkuwi_sdk::templates`].
|
|
||||||
//!
|
|
||||||
//! [`SolochainDefaultConfig`]: struct@pezframe_system::pezpallet::config_preludes::SolochainDefaultConfig
|
|
||||||
//! [`frame`]: crate::pezkuwi_sdk::frame_runtime
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use cmd_lib::run_cmd;
|
|
||||||
|
|
||||||
const FIRST_RUNTIME: &'static str = "pezkuwi-sdk-docs-first-runtime";
|
|
||||||
|
|
||||||
#[docify::export_content]
|
|
||||||
#[test]
|
|
||||||
fn build_runtime() {
|
|
||||||
run_cmd!(
|
|
||||||
cargo build --release -p $FIRST_RUNTIME
|
|
||||||
)
|
|
||||||
.expect("Failed to run command");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
//! # Pezkuwi SDK Docs
|
|
||||||
//!
|
|
||||||
//! The Pezkuwi SDK Developer Documentation.
|
|
||||||
//!
|
|
||||||
//! This crate is a *minimal*, *always-accurate* and low level source of truth about Pezkuwi-SDK.
|
|
||||||
//! For more high level docs, please go to [docs.pezkuwi.com](https://docs.pezkuwichain.io).
|
|
||||||
//!
|
|
||||||
//! ## Getting Started
|
|
||||||
//!
|
|
||||||
//! We suggest the following reading sequence:
|
|
||||||
//!
|
|
||||||
//! - Start by learning about [`pezkuwi_sdk`], its structure and context.
|
|
||||||
//! - Then, head over to the [`guides`]. This modules contains in-depth guides about the most
|
|
||||||
//! important user-journeys of the Pezkuwi SDK.
|
|
||||||
//! - Whilst reading the guides, you might find back-links to [`reference_docs`].
|
|
||||||
//! - [`external_resources`] for a list of 3rd party guides and tutorials.
|
|
||||||
//! - Finally, <https://paritytech.github.io> is the parent website of this crate that contains the
|
|
||||||
//! list of further tools related to the Pezkuwi SDK.
|
|
||||||
//!
|
|
||||||
//! ## Information Architecture
|
|
||||||
//!
|
|
||||||
//! This section paints a picture over the high-level information architecture of this crate.
|
|
||||||
#![doc = simple_mermaid::mermaid!("../../mermaid/IA.mmd")]
|
|
||||||
#![warn(rustdoc::broken_intra_doc_links)]
|
|
||||||
#![warn(rustdoc::private_intra_doc_links)]
|
|
||||||
// Frame macros reference features which this crate does not have
|
|
||||||
#![allow(unexpected_cfgs)]
|
|
||||||
#![doc(html_favicon_url = "https://pezkuwichain.io/favicon.ico")]
|
|
||||||
#![doc(
|
|
||||||
html_logo_url = "https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/docs/images/Polkadot_Logo_Horizontal_Pink_White.png"
|
|
||||||
)]
|
|
||||||
#![doc(issue_tracker_base_url = "https://github.com/pezkuwichain/pezkuwi-sdk/issues")]
|
|
||||||
|
|
||||||
/// Meta information about this crate, how it is built, what principles dictates its evolution and
|
|
||||||
/// how one can contribute to it.
|
|
||||||
pub mod meta_contributing;
|
|
||||||
|
|
||||||
/// A list of external resources and learning material about Pezkuwi SDK.
|
|
||||||
pub mod external_resources;
|
|
||||||
|
|
||||||
/// In-depth guides about the most common components of the Pezkuwi SDK. They are slightly more
|
|
||||||
/// high level and broad than [`reference_docs`].
|
|
||||||
pub mod guides;
|
|
||||||
|
|
||||||
/// An introduction to the Pezkuwi SDK. Read this module to learn about the structure of the SDK,
|
|
||||||
/// the tools that are provided as a part of it, and to gain a high level understanding of each.
|
|
||||||
pub mod pezkuwi_sdk;
|
|
||||||
/// Reference documents covering in-depth topics across the Pezkuwi SDK. It is suggested to read
|
|
||||||
/// these on-demand, while you are going through the [`guides`] or other content.
|
|
||||||
pub mod reference_docs;
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
//! # Contribution
|
|
||||||
//!
|
|
||||||
//! The following sections cover more detailed information about this crate and how it should be
|
|
||||||
//! maintained.
|
|
||||||
//!
|
|
||||||
//! ## Why Rust Docs?
|
|
||||||
//!
|
|
||||||
//! We acknowledge that blockchain based systems, particularly a cutting-edge one like Pezkuwi SDK
|
|
||||||
//! is a software artifact that is complex, and rapidly evolving. This makes the task of documenting
|
|
||||||
//! it externally extremely difficult, especially with regards to making sure it is up-to-date.
|
|
||||||
//!
|
|
||||||
//! Consequently, we argue that the best hedge against this is to move as much of the documentation
|
|
||||||
//! near the source code as possible. This would further incentivize developers to keep the
|
|
||||||
//! documentation up-to-date, as the overhead is reduced by making sure everything is in one
|
|
||||||
//! repository, and everything being in `.rs` files.
|
|
||||||
//!
|
|
||||||
//! > This is not to say that a more visually appealing version of this crate (for example as an
|
|
||||||
//! > `md-book`) cannot exist, but it would be outside the scope of this crate.
|
|
||||||
//!
|
|
||||||
//! Moreover, we acknowledge that a major pain point has been not only outdated *concepts*, but also
|
|
||||||
//! *outdated code*. For this, we commit to making sure no code-snippet in this crate is left as
|
|
||||||
//! `///ignore` or `///no_compile`, making sure all code snippets are self-contained, compile-able,
|
|
||||||
//! and correct at every single revision of the entire repository.
|
|
||||||
//!
|
|
||||||
//! > This also allows us to have a clear versioning on the entire content of this crate. For every
|
|
||||||
//! commit of the Pezkuwi SDK, there would be one version of this crate that is guaranteed to be
|
|
||||||
//! correct.
|
|
||||||
//!
|
|
||||||
//! > To achieve this, we often use [`docify`](https://github.com/sam0x17/docify), a nifty invention
|
|
||||||
//! > of `@sam0x17`.
|
|
||||||
//!
|
|
||||||
//! Also see: <https://github.com/pezkuwichain/pezkuwi-sdk/issues/255>.
|
|
||||||
//!
|
|
||||||
//! ## Scope
|
|
||||||
//!
|
|
||||||
//! The above would NOT be attainable if we don't acknowledge that the scope of this crate MUST be
|
|
||||||
//! limited, or else its maintenance burden would be infeasible or not worthwhile. In short, future
|
|
||||||
//! maintainers should always strive to keep the content of this repository as minimal as possible.
|
|
||||||
//! Some of the following principles are specifically there to be the guidance for this.
|
|
||||||
//!
|
|
||||||
//! ## Principles
|
|
||||||
//!
|
|
||||||
//! The following guidelines are meant to be the guiding torch of those who contribute to this
|
|
||||||
//! crate.
|
|
||||||
//!
|
|
||||||
//! 1. 🔺 Ground Up: Information should be laid out in the most ground-up fashion. The lowest level
|
|
||||||
//! (i.e. "ground") is Rust-docs. The highest level (i.e. "up") is "outside of this crate". In
|
|
||||||
//! between lies [`reference_docs`] and [`guides`], from low to high. The point of this principle
|
|
||||||
//! is to document as much of the information as possible in the lower level media, as it is
|
|
||||||
//! easier to maintain and more reachable. Then, use excessive linking to back-link when writing
|
|
||||||
//! in a more high level.
|
|
||||||
//!
|
|
||||||
//! > A prime example of this, the details of the FRAME storage APIs should NOT be explained in a
|
|
||||||
//! > high level tutorial. They should be explained in the rust-doc of the corresponding type or
|
|
||||||
//! > macro.
|
|
||||||
//!
|
|
||||||
//! 2. 🧘 Less is More: For reasons mentioned [above](#why-rust-docs), the more concise this crate
|
|
||||||
//! is, the better.
|
|
||||||
//! 3. √ Don’t Repeat Yourself – DRY: A summary of the above two points. Authors should always
|
|
||||||
//! strive to avoid any duplicate information. Every concept should ideally be documented in
|
|
||||||
//! *ONE* place and one place only. This makes the task of maintaining topics significantly
|
|
||||||
//! easier.
|
|
||||||
//!
|
|
||||||
//! > A prime example of this, the list of CLI arguments of a particular binary should not be
|
|
||||||
//! > documented in multiple places across this crate. It should be only be documented in the
|
|
||||||
//! > corresponding crate (e.g. `pezsc_cli`).
|
|
||||||
//!
|
|
||||||
//! > Moreover, this means that as a contributor, **it is your responsibility to have a grasp over
|
|
||||||
//! > what topics are already covered in this crate, and how you can build on top of the information
|
|
||||||
//! > that they already pose, rather than repeating yourself**.
|
|
||||||
//!
|
|
||||||
//! For more details see the [latest documenting
|
|
||||||
//! guidelines](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/docs/contributor/DOCUMENTATION_GUIDELINES.md).
|
|
||||||
//!
|
|
||||||
//! #### Example: Explaining `#[pezpallet::call]`
|
|
||||||
//!
|
|
||||||
//! <details>
|
|
||||||
//! <summary>
|
|
||||||
//! Let's consider the seemingly simple example of explaining to someone dead-simple code of a FRAME
|
|
||||||
//! call and see how we can use the above principles.
|
|
||||||
//! </summary>
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #[pezframe::pezpallet(dev_mode)]
|
|
||||||
//! pub mod pezpallet {
|
|
||||||
//! # use pezframe::prelude::*;
|
|
||||||
//! # #[pezpallet::config]
|
|
||||||
//! # pub trait Config: pezframe_system::Config {}
|
|
||||||
//! # #[pezpallet::pezpallet]
|
|
||||||
//! # pub struct Pezpallet<T>(_);
|
|
||||||
//! #[pezpallet::call]
|
|
||||||
//! impl<T: Config> Pezpallet<T> {
|
|
||||||
//! pub fn a_simple_call(origin: OriginFor<T>, data: u32) -> DispatchResult {
|
|
||||||
//! ensure!(data > 10, "SomeStaticString");
|
|
||||||
//! todo!();
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! * Before even getting started, what is with all of this `<T: Config>`? We link to
|
|
||||||
//! [`crate::reference_docs::trait_based_programming`].
|
|
||||||
//! * First, the name. Why is this called `pezpallet::call`? This goes back to `enum Call`, which is
|
|
||||||
//! explained in [`crate::reference_docs::frame_runtime_types`]. Build on top of this!
|
|
||||||
//! * Then, what is `origin`? Just an account id? [`crate::reference_docs::frame_origin`].
|
|
||||||
//! * Then, what is `DispatchResult`? Why is this called *dispatch*? Probably something that can be
|
|
||||||
//! explained in the documentation of [`pezframe::prelude::DispatchResult`].
|
|
||||||
//! * Why is `"SomeStaticString"` a valid error? Because there is implementation for it that you can
|
|
||||||
//! see [here](pezframe::prelude::DispatchError#impl-From<%26'static+str>-for-DispatchError).
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//! All of these are examples of underlying information that a contributor should:
|
|
||||||
//!
|
|
||||||
//! 1. Try and create and they are going along.
|
|
||||||
//! 2. Back-link to if they already exist.
|
|
||||||
//!
|
|
||||||
//! Of course, all of this is not set in stone as a either/or rule. Sometimes, it is necessary to
|
|
||||||
//! rephrase a concept in a new context.
|
|
||||||
//!
|
|
||||||
//! </details>
|
|
||||||
//!
|
|
||||||
//! ## `crates.io` and Publishing
|
|
||||||
//!
|
|
||||||
//! As it stands now, this crate cannot be published to crates.io because of its use of
|
|
||||||
//! [workspace-level `docify`](https://github.com/sam0x17/docify/issues/22). For now, we accept this
|
|
||||||
//! compromise, but in the long term, we should work towards finding a way to maintain different
|
|
||||||
//! revisions of this crate.
|
|
||||||
//!
|
|
||||||
//! ## Versioning
|
|
||||||
//!
|
|
||||||
//! So long as not deployed in `crates.io`, please notice that all of the information in this crate,
|
|
||||||
//! namely in [`crate::guides`] and such are compatible with the master branch of `pezkuwi-sdk`. A
|
|
||||||
//! few solutions have been proposed to improve this, please see
|
|
||||||
//! [here](https://github.com/pezkuwichain/pezkuwi-sdk/issues/289).
|
|
||||||
//!
|
|
||||||
//! ## How to Develop Locally
|
|
||||||
//!
|
|
||||||
//! To view the docs specific [`crate`] locally for development, including the correct HTML headers
|
|
||||||
//! injected, run:
|
|
||||||
//!
|
|
||||||
//! ```sh
|
|
||||||
//! SKIP_WASM_BUILD=1 \
|
|
||||||
//! RUSTDOCFLAGS="--html-in-header $(pwd)/docs/sdk/assets/header.html --extend-css $(pwd)/docs/sdk/assets/theme.css --default-theme=ayu" \
|
|
||||||
//! cargo doc -p pezkuwi-sdk-docs --no-deps --open
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! If even faster build time for docs is needed, you can temporarily remove most of the
|
|
||||||
//! bizinikiwi/pezcumulus dependencies that are only used for linking purposes.
|
|
||||||
//!
|
|
||||||
//! For more on local development, see [`crate::reference_docs::development_environment_advice`].
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
//! # Bizinikiwi
|
|
||||||
//!
|
|
||||||
//! Bizinikiwi is a Rust framework for building blockchains in a modular and extensible way. While
|
|
||||||
//! in itself un-opinionated, it is the main engine behind the Pezkuwi ecosystem.
|
|
||||||
//!
|
|
||||||
//! ## Overview, Philosophy
|
|
||||||
//!
|
|
||||||
//! Bizinikiwi approaches blockchain development with an acknowledgement of a few self-evident
|
|
||||||
//! truths:
|
|
||||||
//!
|
|
||||||
//! 1. Society and technology evolves.
|
|
||||||
//! 2. Humans are fallible.
|
|
||||||
//!
|
|
||||||
//! This makes the task of designing a correct, safe and long-lasting blockchain system hard.
|
|
||||||
//!
|
|
||||||
//! Nonetheless, in strive towards achieving this goal, Bizinikiwi embraces the following:
|
|
||||||
//!
|
|
||||||
//! 1. Use of **Rust** as a modern and safe programming language, which limits human error through
|
|
||||||
//! various means, most notably memory and type safety.
|
|
||||||
//! 2. Bizinikiwi is written from the ground-up with a *generic, modular and extensible* design.
|
|
||||||
//! This ensures that software components can be easily swapped and upgraded. Examples of this is
|
|
||||||
//! multiple consensus mechanisms provided by Bizinikiwi, as listed below.
|
|
||||||
//! 3. Lastly, the final blockchain system created with the above properties needs to be
|
|
||||||
//! upgradeable. In order to achieve this, Bizinikiwi is designed as a meta-protocol, whereby the
|
|
||||||
//! application logic of the blockchain (called "Runtime") is encoded as a WASM blob, and is
|
|
||||||
//! stored in the state. The rest of the system (called "node") acts as the executor of the WASM
|
|
||||||
//! blob.
|
|
||||||
//!
|
|
||||||
//! In essence, the meta-protocol of all Bizinikiwi based chains is the "Runtime as WASM blob"
|
|
||||||
//! accord. This enables the Runtime to become inherently upgradeable, crucially without [forks](https://en.wikipedia.org/wiki/Fork_(blockchain)). The
|
|
||||||
//! upgrade is merely a matter of the WASM blob being changed in the state, which is, in principle,
|
|
||||||
//! same as updating an account's balance. Learn more about this in detail in
|
|
||||||
//! [`crate::reference_docs::wasm_meta_protocol`].
|
|
||||||
//!
|
|
||||||
//! > A great analogy for bizinikiwi is the following: Bizinikiwi node is a gaming console, and a
|
|
||||||
//! > WASM
|
|
||||||
//! > runtime, possibly created with FRAME is the game being inserted into the console.
|
|
||||||
//!
|
|
||||||
//! [`frame`], Bizinikiwi's default runtime development library, takes the above safety practices
|
|
||||||
//! even further by embracing a declarative programming model whereby correctness is enhanced and
|
|
||||||
//! the system is highly configurable through parameterization. Learn more about this in
|
|
||||||
//! [`crate::reference_docs::trait_based_programming`].
|
|
||||||
//!
|
|
||||||
//! ## How to Get Started
|
|
||||||
//!
|
|
||||||
//! Bizinikiwi offers different options at the spectrum of technical freedom <-> development ease.
|
|
||||||
//!
|
|
||||||
//! * The easiest way to use Bizinikiwi is to use one of the templates (some of which listed at
|
|
||||||
//! [`crate::pezkuwi_sdk::templates`]) and only tweak the parameters of the runtime or node. This
|
|
||||||
//! allows you to launch a blockchain in minutes, but is limited in technical freedom.
|
|
||||||
//! * Next, most developers wish to develop their custom runtime modules, for which the de-facto way
|
|
||||||
//! is [`frame`](crate::pezkuwi_sdk::frame_runtime).
|
|
||||||
//! * Finally, Bizinikiwi is highly configurable at the node side as well, but this is the most
|
|
||||||
//! technically demanding.
|
|
||||||
//!
|
|
||||||
//! > A notable Bizinikiwi-based blockchain that has built both custom FRAME pallets and custom
|
|
||||||
//! > node-side components is <https://github.com/Cardinal-Cryptography/aleph-node>.
|
|
||||||
#![doc = simple_mermaid::mermaid!("../../../mermaid/bizinikiwi_dev.mmd")]
|
|
||||||
//!
|
|
||||||
//! ## Structure
|
|
||||||
//!
|
|
||||||
//! Bizinikiwi contains a large number of crates, therefore it is useful to have an overview of what
|
|
||||||
//! they are, and how they are organized. In broad terms, these crates are divided into three
|
|
||||||
//! categories:
|
|
||||||
//!
|
|
||||||
//! * `sc-*` (short for *Bizinikiwi-client*) crates, located under `./client` folder. These are all
|
|
||||||
//! the crates that lead to the node software. Notable examples are [`pezsc_network`], various
|
|
||||||
//! consensus crates, RPC ([`pezsc_rpc_api`]) and database ([`pezsc_client_db`]), all of which are
|
|
||||||
//! expected to reside in the node side.
|
|
||||||
//! * `sp-*` (short for *bizinikiwi-primitives*) crates, located under `./primitives` folder. These
|
|
||||||
//! are crates that facilitate both the node and the runtime, but are not opinionated about what
|
|
||||||
//! framework is using for building the runtime. Notable examples are [`pezsp_api`] and
|
|
||||||
//! [`pezsp_io`], which form the communication bridge between the node and runtime.
|
|
||||||
//! * `pezpallet-*` and `frame-*` crates, located under `./frame` folder. These are the crates
|
|
||||||
//! related to FRAME. See [`frame`] for more information.
|
|
||||||
//!
|
|
||||||
//! ### WASM Build
|
|
||||||
//!
|
|
||||||
//! Many of the Bizinikiwi crates, such as entire `sp-*`, need to compile to both WASM (when a WASM
|
|
||||||
//! runtime is being generated) and native (for example, when testing). To achieve this, Bizinikiwi
|
|
||||||
//! follows the convention of the Rust community, and uses a `feature = "std"` to signify that a
|
|
||||||
//! crate is being built with the standard library, and is built for native. Otherwise, it is built
|
|
||||||
//! for `no_std`.
|
|
||||||
//!
|
|
||||||
//! This can be summarized in `#![cfg_attr(not(feature = "std"), no_std)]`, which you can often find
|
|
||||||
//! in any Bizinikiwi-based runtime.
|
|
||||||
//!
|
|
||||||
//! Bizinikiwi-based runtimes use [`bizinikiwi_wasm_builder`] in their `build.rs` to automatically
|
|
||||||
//! build their WASM files as a part of normal build command (e.g. `cargo build`). Once built, the
|
|
||||||
//! wasm file is placed in `./target/{debug|release}/wbuild/{runtime_name}/{runtime_name}.wasm`.
|
|
||||||
//!
|
|
||||||
//! In order to ensure that the WASM build is **deterministic**, the [Bizinikiwi Runtime Toolbox (srtool)](https://github.com/paritytech/srtool) can be used.
|
|
||||||
//!
|
|
||||||
//! ### Anatomy of a Binary Crate
|
|
||||||
//!
|
|
||||||
//! From the above, [`node_cli`]/[`pez_kitchensink_runtime`] and `node-template` are essentially
|
|
||||||
//! blueprints of a Bizinikiwi-based project, as the name of the latter is implying. Each
|
|
||||||
//! Bizinikiwi-based project typically contains the following:
|
|
||||||
//!
|
|
||||||
//! * Under `./runtime`, a `./runtime/src/lib.rs` which is the top level runtime amalgamator file.
|
|
||||||
//! This file typically contains the [`pezframe::runtime::prelude::construct_runtime`] and
|
|
||||||
//! [`pezframe::runtime::prelude::impl_runtime_apis`] macro calls, which is the final definition of a
|
|
||||||
//! runtime.
|
|
||||||
//!
|
|
||||||
//! * Under `./node`, a `main.rs`, which is the starting point, and a `./service.rs`, which contains
|
|
||||||
//! all the node side components. Skimming this file yields an overview of the networking,
|
|
||||||
//! database, consensus and similar node side components.
|
|
||||||
//!
|
|
||||||
//! > The above two are conventions, not rules.
|
|
||||||
//!
|
|
||||||
//! > See <https://github.com/pezkuwichain/pezkuwi-sdk/issues/241> for an update on how the node side
|
|
||||||
//! > components are being amalgamated.
|
|
||||||
//!
|
|
||||||
//! ## Teyrchain?
|
|
||||||
//!
|
|
||||||
//! As noted above, Bizinikiwi is the main engine behind the Pezkuwi ecosystem. One of the ways
|
|
||||||
//! through which Pezkuwi can be utilized is by building "teyrchains", blockchains that are
|
|
||||||
//! connected to Pezkuwi's shared security.
|
|
||||||
//!
|
|
||||||
//! To build a teyrchain, one could use [Pezcumulus](crate::pezkuwi_sdk::pezcumulus), the library on
|
|
||||||
//! top of Bizinikiwi, empowering any bizinikiwi-based chain to be a Pezkuwi teyrchain.
|
|
||||||
//!
|
|
||||||
//! ## Where To Go Next?
|
|
||||||
//!
|
|
||||||
//! Additional noteworthy crates within bizinikiwi:
|
|
||||||
//!
|
|
||||||
//! - RPC APIs of a Bizinikiwi node: [`pezsc_rpc_api`]/[`pezsc_rpc`]
|
|
||||||
//! - CLI Options of a Bizinikiwi node: [`pezsc_cli`]
|
|
||||||
//! - All of the consensus related crates provided by Bizinikiwi:
|
|
||||||
//! - [`pezsc_consensus_aura`]
|
|
||||||
//! - [`pezsc_consensus_babe`]
|
|
||||||
//! - [`pezsc_consensus_grandpa`]
|
|
||||||
//! - [`pezsc_consensus_beefy`] (TODO: @adrian, add some high level docs <https://github.com/pezkuwichain/pezkuwi-sdk/issues/305>)
|
|
||||||
//! - [`pezsc_consensus_manual_seal`]
|
|
||||||
//! - [`pezsc_consensus_pow`]
|
|
||||||
//!
|
|
||||||
//! [`frame`]: crate::pezkuwi_sdk::frame_runtime
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub use crate::pezkuwi_sdk;
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
//! # FRAME
|
|
||||||
//!
|
|
||||||
//! ```no_compile
|
|
||||||
//! ______ ______ ________ ___ __ __ ______
|
|
||||||
//! /_____/\ /_____/\ /_______/\ /__//_//_/\ /_____/\
|
|
||||||
//! \::::_\/_\:::_ \ \ \::: _ \ \\::\| \| \ \\::::_\/_
|
|
||||||
//! \:\/___/\\:(_) ) )_\::(_) \ \\:. \ \\:\/___/\
|
|
||||||
//! \:::._\/ \: __ `\ \\:: __ \ \\:.\-/\ \ \\::___\/_
|
|
||||||
//! \:\ \ \ \ `\ \ \\:.\ \ \ \\. \ \ \ \\:\____/\
|
|
||||||
//! \_\/ \_\/ \_\/ \__\/\__\/ \__\/ \__\/ \_____\/
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! > **F**ramework for **R**untime **A**ggregation of **M**odularized **E**ntities: Bizinikiwi's
|
|
||||||
//! > State Transition Function (Runtime) Framework.
|
|
||||||
//!
|
|
||||||
//! ## Introduction
|
|
||||||
//!
|
|
||||||
//! As described in [`crate::reference_docs::wasm_meta_protocol`], at a high-level Bizinikiwi-based
|
|
||||||
//! blockchains are composed of two parts:
|
|
||||||
//!
|
|
||||||
//! 1. A *runtime* which represents the state transition function (i.e. "Business Logic") of a
|
|
||||||
//! blockchain, and is encoded as a WASM blob.
|
|
||||||
//! 2. A node whose primary purpose is to execute the given runtime.
|
|
||||||
#![doc = simple_mermaid::mermaid!("../../../mermaid/bizinikiwi_simple.mmd")]
|
|
||||||
//!
|
|
||||||
//! *FRAME is the Bizinikiwi's framework of choice to build a runtime.*
|
|
||||||
//!
|
|
||||||
//! FRAME is composed of two major components, **pallets** and a **runtime**.
|
|
||||||
//!
|
|
||||||
//! ## Pallets
|
|
||||||
//!
|
|
||||||
//! A pezpallet is a unit of encapsulated logic. It has a clearly defined responsibility and can be
|
|
||||||
//! linked to other pallets. In order to be reusable, pallets shipped with FRAME strive to only care
|
|
||||||
//! about its own responsibilities and make as few assumptions about the general runtime as
|
|
||||||
//! possible. A pezpallet is analogous to a _module_ in the runtime.
|
|
||||||
//!
|
|
||||||
//! A pezpallet is defined as a `mod pezpallet` wrapped by the [`pezframe::pezpallet`] macro. Within
|
|
||||||
//! this macro, pezpallet components/parts can be defined. Most notable of these parts are:
|
|
||||||
//!
|
|
||||||
//! - [Config](pezframe::pezpallet_macros::config), allowing a pezpallet to make itself configurable
|
|
||||||
//! and generic over types, values and such.
|
|
||||||
//! - [Storage](pezframe::pezpallet_macros::storage), allowing a pezpallet to define onchain storage.
|
|
||||||
//! - [Dispatchable function](pezframe::pezpallet_macros::call), allowing a pezpallet to define
|
|
||||||
//! extrinsics that are callable by end users, from the outer world.
|
|
||||||
//! - [Events](pezframe::pezpallet_macros::event), allowing a pezpallet to emit events.
|
|
||||||
//! - [Errors](pezframe::pezpallet_macros::error), allowing a pezpallet to emit well-formed errors.
|
|
||||||
//!
|
|
||||||
//! Some of these pezpallet components resemble the building blocks of a smart contract. While both
|
|
||||||
//! models are programming state transition functions of blockchains, there are crucial differences
|
|
||||||
//! between the two. See [`crate::reference_docs::runtime_vs_smart_contract`] for more.
|
|
||||||
//!
|
|
||||||
//! Most of these components are defined using macros, the full list of which can be found in
|
|
||||||
//! [`pezframe::pezpallet_macros`].
|
|
||||||
//!
|
|
||||||
//! ### Example
|
|
||||||
//!
|
|
||||||
//! The following example showcases a minimal pezpallet.
|
|
||||||
#![doc = docify::embed!("src/pezkuwi_sdk/frame_runtime.rs", pezpallet)]
|
|
||||||
//!
|
|
||||||
//! ## Runtime
|
|
||||||
//!
|
|
||||||
//! A runtime is a collection of pallets that are amalgamated together. Each pezpallet typically has
|
|
||||||
//! some configurations (exposed as a `trait Config`) that needs to be *specified* in the runtime.
|
|
||||||
//! This is done with [`pezframe::runtime::prelude::construct_runtime`].
|
|
||||||
//!
|
|
||||||
//! A (real) runtime that actually wishes to compile to WASM needs to also implement a set of
|
|
||||||
//! runtime-apis. These implementation can be specified using the
|
|
||||||
//! [`pezframe::runtime::prelude::impl_runtime_apis`] macro.
|
|
||||||
//!
|
|
||||||
//! ### Example
|
|
||||||
//!
|
|
||||||
//! The following example shows a (test) runtime that is composing the pezpallet demonstrated above,
|
|
||||||
//! next to the [`pezframe::prelude::pezframe_system`] pezpallet, into a runtime.
|
|
||||||
#![doc = docify::embed!("src/pezkuwi_sdk/frame_runtime.rs", runtime)]
|
|
||||||
//!
|
|
||||||
//! ## More Examples
|
|
||||||
//!
|
|
||||||
//! You can find more FRAME examples that revolve around specific features at
|
|
||||||
//! [`pezpallet_examples`].
|
|
||||||
//!
|
|
||||||
//! ## Alternatives 🌈
|
|
||||||
//!
|
|
||||||
//! There is nothing in the Bizinikiwi's node side code-base that mandates the use of FRAME. While
|
|
||||||
//! FRAME makes it very simple to write Bizinikiwi-based runtimes, it is by no means intended to be
|
|
||||||
//! the only one. At the end of the day, any WASM blob that exposes the right set of runtime APIs is
|
|
||||||
//! a valid Runtime form the point of view of a Bizinikiwi client (see
|
|
||||||
//! [`crate::reference_docs::wasm_meta_protocol`]). Notable examples are:
|
|
||||||
//!
|
|
||||||
//! * writing a runtime in pure Rust, as done in [this template](https://github.com/JoshOrndorff/frameless-node-template).
|
|
||||||
//! * writing a runtime in AssemblyScript, as explored in [this project](https://github.com/LimeChain/subsembly).
|
|
||||||
|
|
||||||
/// A FRAME based pezpallet. This `mod` is the entry point for everything else. All
|
|
||||||
/// `#[pezpallet::xxx]` macros must be defined in this `mod`. Although, frame also provides an
|
|
||||||
/// experimental feature to break these parts into different `mod`s. See [`pezpallet_examples`] for
|
|
||||||
/// more.
|
|
||||||
#[docify::export]
|
|
||||||
#[pezframe::pezpallet(dev_mode)]
|
|
||||||
pub mod pezpallet {
|
|
||||||
use pezframe::prelude::*;
|
|
||||||
|
|
||||||
/// The configuration trait of a pezpallet. Mandatory. Allows a pezpallet to receive types at a
|
|
||||||
/// later point from the runtime that wishes to contain it. It allows the pezpallet to be
|
|
||||||
/// parameterized over both types and values.
|
|
||||||
#[pezpallet::config]
|
|
||||||
pub trait Config: pezframe_system::Config {
|
|
||||||
/// A type that is not known now, but the runtime that will contain this pezpallet will
|
|
||||||
/// know it later, therefore we define it here as an associated type.
|
|
||||||
#[allow(deprecated)]
|
|
||||||
type RuntimeEvent: IsType<<Self as pezframe_system::Config>::RuntimeEvent>
|
|
||||||
+ From<Event<Self>>;
|
|
||||||
|
|
||||||
/// A parameterize-able value that we receive later via the `Get<_>` trait.
|
|
||||||
type ValueParameter: Get<u32>;
|
|
||||||
|
|
||||||
/// Similar to [`Config::ValueParameter`], but using `const`. Both are functionally
|
|
||||||
/// equal, but offer different tradeoffs.
|
|
||||||
const ANOTHER_VALUE_PARAMETER: u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A mandatory struct in each pezpallet. All functions callable by external users (aka.
|
|
||||||
/// transactions) must be attached to this type (see [`pezframe::pezpallet_macros::call`]). For
|
|
||||||
/// convenience, internal (private) functions can also be attached to this type.
|
|
||||||
#[pezpallet::pezpallet]
|
|
||||||
pub struct Pezpallet<T>(PhantomData<T>);
|
|
||||||
|
|
||||||
/// The events that this pezpallet can emit.
|
|
||||||
#[pezpallet::event]
|
|
||||||
pub enum Event<T: Config> {}
|
|
||||||
|
|
||||||
/// A storage item that this pezpallet contains. This will be part of the state root trie
|
|
||||||
/// of the blockchain.
|
|
||||||
#[pezpallet::storage]
|
|
||||||
pub type Value<T> = StorageValue<Value = u32>;
|
|
||||||
|
|
||||||
/// All *dispatchable* call functions (aka. transactions) are attached to `Pezpallet` in a
|
|
||||||
/// `impl` block.
|
|
||||||
#[pezpallet::call]
|
|
||||||
impl<T: Config> Pezpallet<T> {
|
|
||||||
/// This will be callable by external users, and has two u32s as a parameter.
|
|
||||||
pub fn some_dispatchable(
|
|
||||||
_origin: OriginFor<T>,
|
|
||||||
_param: u32,
|
|
||||||
_other_para: u32,
|
|
||||||
) -> DispatchResult {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A simple runtime that contains the above pezpallet and `pezframe_system`, the mandatory
|
|
||||||
/// pezpallet of all runtimes. This runtime is for testing, but it shares a lot of similarities with
|
|
||||||
/// a *real* runtime.
|
|
||||||
#[docify::export]
|
|
||||||
pub mod runtime {
|
|
||||||
use super::pezpallet as pezpallet_example;
|
|
||||||
use pezframe::{prelude::*, testing_prelude::*};
|
|
||||||
|
|
||||||
// The major macro that amalgamates pallets into `enum Runtime`
|
|
||||||
construct_runtime!(
|
|
||||||
pub enum Runtime {
|
|
||||||
System: pezframe_system,
|
|
||||||
Example: pezpallet_example,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// These `impl` blocks specify the parameters of each pezpallet's `trait Config`.
|
|
||||||
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
|
|
||||||
impl pezframe_system::Config for Runtime {
|
|
||||||
type Block = MockBlock<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pezpallet_example::Config for Runtime {
|
|
||||||
type RuntimeEvent = RuntimeEvent;
|
|
||||||
type ValueParameter = ConstU32<42>;
|
|
||||||
const ANOTHER_VALUE_PARAMETER: u32 = 42;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
//! # Pezkuwi SDK
|
|
||||||
//!
|
|
||||||
//! [Pezkuwi SDK](https://github.com/pezkuwichain/pezkuwi-sdk) provides the main resources needed to
|
|
||||||
//! start building on the [Pezkuwi network](https://pezkuwichain.io/), a scalable, multi-chain
|
|
||||||
//! blockchain platform that enables different blockchains to securely interoperate.
|
|
||||||
//!
|
|
||||||
//! [](https://exchange.pezkuwichain.app/)
|
|
||||||
//!
|
|
||||||
//! [](https://github.com/Awsmdot/awesome-dot)
|
|
||||||
//! [](https://wiki.network.pezkuwichain.io/)
|
|
||||||
//! [](https://forum.polkadot.network/)
|
|
||||||
//!
|
|
||||||
//! [](https://github.com/polkadot-fellows/rfcs)
|
|
||||||
//! [](https://github.com/polkadot-fellows/runtimes)
|
|
||||||
//! [](https://github.com/polkadot-fellows/manifesto/blob/main/manifesto.pdf)
|
|
||||||
//!
|
|
||||||
//! ## Getting Started
|
|
||||||
//!
|
|
||||||
//! The primary way to get started with the Pezkuwi SDK is to start writing a FRAME-based runtime.
|
|
||||||
//! See:
|
|
||||||
//!
|
|
||||||
//! * [`pezkuwi`], to understand what is Pezkuwi as a development platform.
|
|
||||||
//! * [`bizinikiwi`], for an overview of what Bizinikiwi as the main blockchain framework of Pezkuwi
|
|
||||||
//! SDK.
|
|
||||||
//! * [`frame`], to learn about how to write blockchain applications aka. "App Chains".
|
|
||||||
//! * Continue with the [`pezkuwi_sdk_docs`'s "getting started"](crate#getting-started).
|
|
||||||
//!
|
|
||||||
//! ## Components
|
|
||||||
//!
|
|
||||||
//! #### Bizinikiwi
|
|
||||||
//!
|
|
||||||
//! [](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/bizinikiwi/LICENSE-APACHE2)
|
|
||||||
//! [](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/bizinikiwi)
|
|
||||||
//!
|
|
||||||
//! [`bizinikiwi`] is the base blockchain framework used to power the Pezkuwi SDK. It is a full
|
|
||||||
//! toolkit to create sovereign blockchains, including but not limited to those which connect to
|
|
||||||
//! Pezkuwi as teyrchains.
|
|
||||||
//!
|
|
||||||
//! #### FRAME
|
|
||||||
//!
|
|
||||||
//! [](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/bizinikiwi/LICENSE-APACHE2)
|
|
||||||
//! [](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/bizinikiwi/pezframe)
|
|
||||||
//!
|
|
||||||
//! [`frame`] is the framework used to create Bizinikiwi-based application logic, aka. runtimes.
|
|
||||||
//! Learn more about the distinction of a runtime and node in
|
|
||||||
//! [`reference_docs::wasm_meta_protocol`].
|
|
||||||
//!
|
|
||||||
//! #### Pezcumulus
|
|
||||||
//!
|
|
||||||
//! [](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/pezcumulus/LICENSE)
|
|
||||||
//! [](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/pezcumulus)
|
|
||||||
//!
|
|
||||||
//! [`pezcumulus`] transforms FRAME-based runtimes into Pezkuwi-compatible teyrchain runtimes, and
|
|
||||||
//! Bizinikiwi-based nodes into Pezkuwi/Teyrchain-compatible nodes.
|
|
||||||
//!
|
|
||||||
//! #### XCM
|
|
||||||
//!
|
|
||||||
//! [](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/pezkuwi/LICENSE)
|
|
||||||
//! [](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/pezkuwi/xcm)
|
|
||||||
//!
|
|
||||||
//! [`xcm`], short for "cross consensus message", is the primary format that is used for
|
|
||||||
//! communication between teyrchains, but is intended to be extensible to other use cases as well.
|
|
||||||
//!
|
|
||||||
//! #### Pezkuwi
|
|
||||||
//!
|
|
||||||
//! [](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/pezkuwi/LICENSE)
|
|
||||||
//! [](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/pezkuwi)
|
|
||||||
//!
|
|
||||||
//! [`pezkuwi`] is an implementation of a Pezkuwi node in Rust, by `@paritytech`. The Pezkuwi
|
|
||||||
//! runtimes are located under the
|
|
||||||
//! [`pezkuwi-fellows/runtimes`](https://github.com/polkadot-fellows/runtimes) repository.
|
|
||||||
//!
|
|
||||||
//! ### Binaries
|
|
||||||
//!
|
|
||||||
//! The main binaries that are part of the Pezkuwi SDK are:
|
|
||||||
|
|
||||||
//! * [`pezkuwi`]: The Pezkuwi relay chain node binary, as noted above.
|
|
||||||
//! * [`pezkuwi-omni-node`]: A white-labeled teyrchain collator node. See more in
|
|
||||||
//! [`crate::reference_docs::omni_node`].
|
|
||||||
//! * [`pezkuwi-teyrchain-bin`]: The collator node used to run collators for all Pezkuwi system
|
|
||||||
//! teyrchains.
|
|
||||||
//! * [`frame-omni-bencher`]: a benchmarking tool for FRAME-based runtimes. Nodes typically contain
|
|
||||||
//! a
|
|
||||||
//! `benchmark` subcommand that does the same.
|
|
||||||
//! * [`chain_spec_builder`]: Utility to build chain-specs Nodes typically contain a `build-spec`
|
|
||||||
//! subcommand that does the same.
|
|
||||||
//! * [`pez_subkey`]: Bizinikiwi's key management utility.
|
|
||||||
//! * [`bizinikiwi-node`](node_cli) is an extensive bizinikiwi node that contains the superset of
|
|
||||||
//! all runtime and node side features. The corresponding runtime, called
|
|
||||||
//! [`pez_kitchensink_runtime`] contains all of the modules that are provided with `FRAME`. This
|
|
||||||
//! node and runtime is only used for testing and demonstration.
|
|
||||||
//!
|
|
||||||
//! ### Summary
|
|
||||||
//!
|
|
||||||
//! The following diagram summarizes how some of the components of Pezkuwi SDK work together:
|
|
||||||
#![doc = simple_mermaid::mermaid!("../../../mermaid/pezkuwi_sdk_bizinikiwi.mmd")]
|
|
||||||
//!
|
|
||||||
//! A Bizinikiwi-based chain is a blockchain composed of a runtime and a node. As noted above, the
|
|
||||||
//! runtime is the application logic of the blockchain, and the node is everything else.
|
|
||||||
//! See [`reference_docs::wasm_meta_protocol`] for an in-depth explanation of this. The
|
|
||||||
//! former is built with [`frame`], and the latter is built with rest of Bizinikiwi.
|
|
||||||
//!
|
|
||||||
//! > You can think of a Bizinikiwi-based chain as a white-labeled blockchain.
|
|
||||||
#![doc = simple_mermaid::mermaid!("../../../mermaid/pezkuwi_sdk_pezkuwi.mmd")]
|
|
||||||
//! Pezkuwi is itself a Bizinikiwi-based chain, composed of the exact same two components. It has
|
|
||||||
//! specialized logic in both the node and the runtime side, but it is not "special" in any way.
|
|
||||||
//!
|
|
||||||
//! A teyrchain is a "special" Bizinikiwi-based chain, whereby both the node and the runtime
|
|
||||||
//! components have became "Pezkuwi-aware" using Pezcumulus.
|
|
||||||
#![doc = simple_mermaid::mermaid!("../../../mermaid/pezkuwi_sdk_teyrchain.mmd")]
|
|
||||||
//!
|
|
||||||
//! ## Notable Upstream Crates
|
|
||||||
//!
|
|
||||||
//! - [`parity-scale-codec`](https://github.com/pezkuwichain/parity-scale-codec)
|
|
||||||
//! - [`parity-db`](https://github.com/pezkuwichain/parity-db)
|
|
||||||
//! - [`trie`](https://github.com/paritytech/trie)
|
|
||||||
//! - [`parity-common`](https://github.com/paritytech/parity-common)
|
|
||||||
//!
|
|
||||||
//! ## Trophy Section: Notable Downstream Projects
|
|
||||||
//!
|
|
||||||
//! A list of projects and tools in the blockchain ecosystem that one way or another use parts of
|
|
||||||
//! the Pezkuwi SDK:
|
|
||||||
//!
|
|
||||||
//! * [Avail](https://github.com/availproject/avail)
|
|
||||||
//! * [Cardano Partner Chains](https://iohk.io/en/blog/posts/2023/11/03/partner-chains-are-coming-to-cardano/)
|
|
||||||
//! * [Starknet's Madara Sequencer](https://github.com/keep-starknet-strange/madara)
|
|
||||||
//! * [Polymesh](https://polymesh.network/)
|
|
||||||
//!
|
|
||||||
//! [`bizinikiwi`]: crate::pezkuwi_sdk::bizinikiwi
|
|
||||||
//! [`frame`]: crate::pezkuwi_sdk::frame_runtime
|
|
||||||
//! [`pezcumulus`]: crate::pezkuwi_sdk::pezcumulus
|
|
||||||
//! [`pezkuwi`]: crate::pezkuwi_sdk::pezkuwi
|
|
||||||
//! [`xcm`]: crate::pezkuwi_sdk::xcm
|
|
||||||
//! [`frame-omni-bencher`]: https://crates.io/crates/frame-omni-bencher
|
|
||||||
//! [`pezkuwi-teyrchain-bin`]: https://crates.io/crates/polkadot-parachain-bin
|
|
||||||
//! [`pezkuwi-omni-node`]: https://crates.io/crates/polkadot-omni-node
|
|
||||||
|
|
||||||
/// Learn about Bizinikiwi, the main blockchain framework used in the Pezkuwi ecosystem.
|
|
||||||
pub mod bizinikiwi;
|
|
||||||
/// Learn about FRAME, the framework used to build Bizinikiwi runtimes.
|
|
||||||
pub mod frame_runtime;
|
|
||||||
/// Learn about Pezcumulus, the framework that transforms [`bizinikiwi`]-based chains into
|
|
||||||
/// [`pezkuwi`]-enabled teyrchains.
|
|
||||||
pub mod pezcumulus;
|
|
||||||
/// Learn about Pezkuwi as a platform.
|
|
||||||
pub mod pezkuwi;
|
|
||||||
/// Learn about different ways through which smart contracts can be utilized on top of Bizinikiwi,
|
|
||||||
/// and in the Pezkuwi ecosystem.
|
|
||||||
pub mod smart_contracts;
|
|
||||||
/// Index of all the templates that can act as first scaffold for a new project.
|
|
||||||
pub mod templates;
|
|
||||||
/// Learn about XCM, the de-facto communication language between different consensus systems.
|
|
||||||
pub mod xcm;
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
//! # Pezcumulus
|
|
||||||
//!
|
|
||||||
//! Bizinikiwi provides a framework ([FRAME]) through which a blockchain node and runtime can easily
|
|
||||||
//! be created. Pezcumulus aims to extend the same approach to creation of Pezkuwi teyrchains.
|
|
||||||
//!
|
|
||||||
//! > Pezcumulus clouds are shaped sort of like dots; together they form a system that is intricate,
|
|
||||||
//! > beautiful and functional.
|
|
||||||
//!
|
|
||||||
//! ## Example: Runtime
|
|
||||||
//!
|
|
||||||
//! A Pezcumulus-based runtime is fairly similar to other [FRAME]-based runtimes. Most notably, the
|
|
||||||
//! following changes are applied to a normal FRAME-based runtime to make it a Pezcumulus-based
|
|
||||||
//! runtime:
|
|
||||||
//!
|
|
||||||
//! #### Pezcumulus Pallets
|
|
||||||
//!
|
|
||||||
//! A teyrchain runtime should use a number of pallets that are provided by Pezcumulus and
|
|
||||||
//! Bizinikiwi. Notably:
|
|
||||||
//!
|
|
||||||
//! - [`pezframe-system`](pezframe::prelude::pezframe_system), like all FRAME-based runtimes.
|
|
||||||
//! - [`pezcumulus_pezpallet_teyrchain_system`]
|
|
||||||
//! - [`teyrchain_info`]
|
|
||||||
#![doc = docify::embed!("./src/pezkuwi_sdk/pezcumulus.rs", system_pallets)]
|
|
||||||
//!
|
|
||||||
//! Given that all Pezcumulus-based runtimes use a simple Aura-based consensus mechanism, the
|
|
||||||
//! following pallets also need to be added:
|
|
||||||
//!
|
|
||||||
//! - [`pezpallet_timestamp`]
|
|
||||||
//! - [`pezpallet_aura`]
|
|
||||||
//! - [`pezcumulus_pezpallet_aura_ext`]
|
|
||||||
#![doc = docify::embed!("./src/pezkuwi_sdk/pezcumulus.rs", consensus_pallets)]
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//! Finally, a separate macro, similar to
|
|
||||||
//! [`impl_runtime_api`](pezframe::runtime::prelude::impl_runtime_apis), which creates the default set
|
|
||||||
//! of runtime APIs, will generate the teyrchain runtime's validation runtime API, also known as
|
|
||||||
//! teyrchain validation function (PVF). Without this API, the relay chain is unable to validate
|
|
||||||
//! blocks produced by our teyrchain.
|
|
||||||
#![doc = docify::embed!("./src/pezkuwi_sdk/pezcumulus.rs", validate_block)]
|
|
||||||
//!
|
|
||||||
//! ---
|
|
||||||
//!
|
|
||||||
//! [FRAME]: crate::pezkuwi_sdk::frame_runtime
|
|
||||||
|
|
||||||
#![deny(rustdoc::broken_intra_doc_links)]
|
|
||||||
#![deny(rustdoc::private_intra_doc_links)]
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
mod runtime {
|
|
||||||
pub use pezframe::{
|
|
||||||
deps::pezsp_consensus_aura::sr25519::AuthorityId as AuraId, prelude::*,
|
|
||||||
runtime::prelude::*, testing_prelude::*,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[docify::export(CR)]
|
|
||||||
construct_runtime!(
|
|
||||||
pub enum Runtime {
|
|
||||||
// system-level pallets.
|
|
||||||
System: pezframe_system,
|
|
||||||
Timestamp: pezpallet_timestamp,
|
|
||||||
TeyrchainSystem: pezcumulus_pezpallet_teyrchain_system,
|
|
||||||
TeyrchainInfo: teyrchain_info,
|
|
||||||
|
|
||||||
// teyrchain consensus support -- mandatory.
|
|
||||||
Aura: pezpallet_aura,
|
|
||||||
AuraExt: pezcumulus_pezpallet_aura_ext,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
mod system_pallets {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
|
|
||||||
impl pezframe_system::Config for Runtime {
|
|
||||||
type Block = MockBlock<Self>;
|
|
||||||
type OnSetCode = pezcumulus_pezpallet_teyrchain_system::TeyrchainSetCode<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pezcumulus_pezpallet_teyrchain_system::Config for Runtime {
|
|
||||||
type RuntimeEvent = RuntimeEvent;
|
|
||||||
type OnSystemEvent = ();
|
|
||||||
type SelfParaId = teyrchain_info::Pezpallet<Runtime>;
|
|
||||||
type OutboundXcmpMessageSource = ();
|
|
||||||
type XcmpMessageHandler = ();
|
|
||||||
type ReservedDmpWeight = ();
|
|
||||||
type ReservedXcmpWeight = ();
|
|
||||||
type CheckAssociatedRelayNumber =
|
|
||||||
pezcumulus_pezpallet_teyrchain_system::RelayNumberMonotonicallyIncreases;
|
|
||||||
type ConsensusHook = pezcumulus_pezpallet_aura_ext::FixedVelocityConsensusHook<
|
|
||||||
Runtime,
|
|
||||||
6000, // relay chain block time
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
>;
|
|
||||||
type WeightInfo = ();
|
|
||||||
type DmpQueue = pezframe::traits::EnqueueWithOrigin<(), pezsp_core::ConstU8<0>>;
|
|
||||||
type RelayParentOffset = ConstU32<0>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl teyrchain_info::Config for Runtime {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
mod consensus_pallets {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
impl pezpallet_aura::Config for Runtime {
|
|
||||||
type AuthorityId = AuraId;
|
|
||||||
type DisabledValidators = ();
|
|
||||||
type MaxAuthorities = ConstU32<100_000>;
|
|
||||||
type AllowMultipleBlocksPerSlot = ConstBool<false>;
|
|
||||||
type SlotDuration = pezpallet_aura::MinimumPeriodTimesTwo<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export(timestamp)]
|
|
||||||
#[derive_impl(pezpallet_timestamp::config_preludes::TestDefaultConfig)]
|
|
||||||
impl pezpallet_timestamp::Config for Runtime {}
|
|
||||||
|
|
||||||
impl pezcumulus_pezpallet_aura_ext::Config for Runtime {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export(validate_block)]
|
|
||||||
pezcumulus_pezpallet_teyrchain_system::register_validate_block! {
|
|
||||||
Runtime = Runtime,
|
|
||||||
BlockExecutor = pezcumulus_pezpallet_aura_ext::BlockExecutor::<Runtime, Executive>,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
//! # Pezkuwi
|
|
||||||
//!
|
|
||||||
//! Implementation of the Pezkuwi node/host in Rust.
|
|
||||||
//!
|
|
||||||
//! ## Learn More and Get Involved
|
|
||||||
//!
|
|
||||||
//! - [Pezkuwi Forum](https://forum.polkadot.network/)
|
|
||||||
//! - [Pezkuwi Teyrchains](https://parachains.info/)
|
|
||||||
//! - [Pezkuwi (multi-chain) Explorer: Subscan](https://subscan.io/)
|
|
||||||
//! - Pezkuwi Fellowship
|
|
||||||
//! - [Manifesto](https://github.com/polkadot-fellows/manifesto/blob/main/manifesto.pdf)
|
|
||||||
//! - [Runtimes](https://github.com/polkadot-fellows/runtimes)
|
|
||||||
//! - [RFCs](https://github.com/polkadot-fellows/rfcs)
|
|
||||||
//! - [Dashboard](https://polkadot-fellows.github.io/dashboard/)
|
|
||||||
//! - [Pezkuwi Specs](http://spec.polkadot.network)
|
|
||||||
//! - [The Pezkuwi Teyrchain Host Implementers' Guide](https://docs.pezkuwichain.io/sdk/book/)
|
|
||||||
//! - [Pezkuwi papers](https://pezkuwichain.io/papers/)
|
|
||||||
//! - [JAM Graypaper](https://graypaper.com)
|
|
||||||
//!
|
|
||||||
//! ## Alternative Node Implementations 🌈
|
|
||||||
//!
|
|
||||||
//! - [Smoldot](https://docs.rs/crate/smoldot-light/latest). Pezkuwi light node/client.
|
|
||||||
//! - [KAGOME](https://github.com/qdrvm/kagome). C++ implementation of the Pezkuwi host.
|
|
||||||
//! - [Gossamer](https://github.com/ChainSafe/gossamer). Golang implementation of the Pezkuwi host.
|
|
||||||
//!
|
|
||||||
//! ## Platform
|
|
||||||
//!
|
|
||||||
//! In this section, we examine what platform Pezkuwi exactly provides to developers.
|
|
||||||
//!
|
|
||||||
//! ### Pezkuwi White Paper
|
|
||||||
//!
|
|
||||||
//! The original vision of Pezkuwi (everything in the whitepaper, which was eventually called
|
|
||||||
//! **Pezkuwi 1.0**) revolves around the following arguments:
|
|
||||||
//!
|
|
||||||
//! * Future is multi-chain, because we need different chains with different specialization to
|
|
||||||
//! achieve widespread goals.
|
|
||||||
//! * In other words, no single chain is good enough to achieve all goals.
|
|
||||||
//! * A multi-chain future will inadvertently suffer from fragmentation of economic security.
|
|
||||||
//! * This stake fragmentation will make communication over consensus system with varying security
|
|
||||||
//! levels inherently unsafe.
|
|
||||||
//!
|
|
||||||
//! Pezkuwi's answer to the above is:
|
|
||||||
//!
|
|
||||||
//! > The chains of the future must have a way to share their economic security, whilst maintaining
|
|
||||||
//! > their execution and governance sovereignty. These chains are called "Teyrchains".
|
|
||||||
//!
|
|
||||||
//! * Shared Security: The idea of shared economic security sits at the core of Pezkuwi. Pezkuwi
|
|
||||||
//! enables different teyrchains to pool their economic security from Pezkuwi (i.e. "*Relay
|
|
||||||
//! Chain*").
|
|
||||||
//! * (heterogenous) Sharded Execution: Yet, each teyrchain is free to have its own execution logic
|
|
||||||
//! (runtime), which also encompasses governance and sovereignty. Moreover, Pezkuwi ensures the
|
|
||||||
//! correct execution of all teyrchains, without having all of its validators re-execute all
|
|
||||||
//! teyrchain blocks. When seen from this perspective, Pezkuwi achieves the ability to verify
|
|
||||||
//! the validity of the block execution of multiple teyrchains using the same set of validators as
|
|
||||||
//! the Relay Chain. In practice, this means that the shards (teyrchains) share the same economic
|
|
||||||
//! security as the Relay Chain.
|
|
||||||
//! Learn about this process called [Approval Checking](https://pezkuwichain.io/blog/polkadot-v1-0-sharding-and-economic-security#approval-checking-and-finality).
|
|
||||||
//! * A framework to build blockchains: In order to materialize the ecosystem of teyrchains, an easy
|
|
||||||
//! blockchain framework must exist. This is [Bizinikiwi](crate::pezkuwi_sdk::bizinikiwi),
|
|
||||||
//! [FRAME](crate::pezkuwi_sdk::frame_runtime) and [Pezcumulus](crate::pezkuwi_sdk::pezcumulus).
|
|
||||||
//! * A communication language between blockchains: In order for these blockchains to communicate,
|
|
||||||
//! they need a shared language. [XCM](crate::pezkuwi_sdk::xcm) is one such language, and the one
|
|
||||||
//! that is most endorsed in the Pezkuwi ecosystem.
|
|
||||||
//!
|
|
||||||
//! > Note that the interoperability promised by Pezkuwi is unparalleled in that any two teyrchains
|
|
||||||
//! > connected to Pezkuwi have the same security and can have much better guarantees about the
|
|
||||||
//! > security of the recipient of any message.
|
|
||||||
//! > Bridges enable transaction and information flow between different consensus systems, crucial
|
|
||||||
//! > for Pezkuwi's multi-chain architecture. However, they can become the network's most
|
|
||||||
//! > vulnerable points. If a bridge's security measures are weaker than those of the connected
|
|
||||||
//! > blockchains, it poses a significant risk. Attackers might exploit these weaknesses to launch
|
|
||||||
//! > attacks such as theft or disruption of services.
|
|
||||||
//!
|
|
||||||
//! Pezkuwi delivers the above vision, alongside a flexible means for teyrchains to schedule
|
|
||||||
//! themselves with the Relay Chain. To achieve this, Pezkuwi has been developed with an
|
|
||||||
//! architecture similar to that of a computer. Pezkuwi Relay Chain has a number of "cores". Each
|
|
||||||
//! core is (in simple terms) capable of progressing 1 teyrchain at a time. For example, a teyrchain
|
|
||||||
//! can schedule itself on a single core for 5 relay chain blocks.
|
|
||||||
//!
|
|
||||||
//! Within the scope of Pezkuwi 1.x, two main scheduling ways have been considered:
|
|
||||||
//!
|
|
||||||
//! * Long term Teyrchains, obtained through locking a sum of HEZ in an auction system.
|
|
||||||
//! * On-demand Teyrchains, purchased through paying HEZ to the relay-chain whenever needed.
|
|
||||||
//!
|
|
||||||
//! ### The Future
|
|
||||||
//!
|
|
||||||
//! After delivering Pezkuwi 1.x, the future of Pezkuwi as a protocol and platform is in the hands
|
|
||||||
//! of the community and the fellowship. This is happening most notable through the RFC process.
|
|
||||||
//! Some of the RFCs that do alter Pezkuwi as a platform and have already passed are as follows:
|
|
||||||
//!
|
|
||||||
//! - RFC#1: [Agile-coretime](https://github.com/polkadot-fellows/RFCs/blob/main/text/0001-agile-coretime.md):
|
|
||||||
//! Agile periodic-sale-based model for assigning Coretime on the Pezkuwi Ubiquitous Computer.
|
|
||||||
//! - RFC#5: [Coretime-interface](https://github.com/polkadot-fellows/RFCs/blob/main/text/0005-coretime-interface.md):
|
|
||||||
//! Interface for manipulating the usage of cores on the Pezkuwi Ubiquitous Computer.
|
|
||||||
//!
|
|
||||||
//! Learn more about [Pezkuwi as a Computational Resource](https://wiki.network.pezkuwichain.io/docs/polkadot-direction#polkadot-as-a-computational-resource).
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
//! # Smart Contracts
|
|
||||||
//!
|
|
||||||
//! TODO: @cmichi <https://github.com/pezkuwichain/pezkuwi-sdk/issues/304>
|
|
||||||
//!
|
|
||||||
//! - WASM and EVM based, pezpallet-contracts and pezpallet-evm.
|
|
||||||
//! - single-daap-chain, transition from ink! to FRAME.
|
|
||||||
//! - Link to `use.ink`
|
|
||||||
//! - Link to [`crate::reference_docs::runtime_vs_smart_contract`].
|
|
||||||
//! - <https://use.ink/migrate-ink-contracts-to-polkadot-frame-parachain/>
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
//! # Templates
|
|
||||||
//!
|
|
||||||
//! This document enumerates a non-exhaustive list of templates that one can use to get started with
|
|
||||||
//! pezkuwi-sdk.
|
|
||||||
//!
|
|
||||||
//! > Know more tools/templates that are not listed here? please contribute them by opening a PR.
|
|
||||||
//!
|
|
||||||
//! ## Internal
|
|
||||||
//!
|
|
||||||
//! The following templates are maintained as a part of the `pezkuwi-sdk` repository:
|
|
||||||
//!
|
|
||||||
//! - [`minimal-template`](https://github.com/pezkuwichain/pezkuwi-sdk/issues/195): A minimal
|
|
||||||
//! template that contains the least amount of features to be a functioning blockchain. Suitable
|
|
||||||
//! for learning and testing.
|
|
||||||
//! - [`solochain-template`](https://github.com/pezkuwichain/pezkuwi-sdk/issues/195): Formerly known
|
|
||||||
//! as "bizinikiwi-node-template", is a white-labeled bizinikiwi-based blockchain (aka. solochain)
|
|
||||||
//! that contains moderate features, such as a basic consensus engine and some FRAME pallets. This
|
|
||||||
//! template can act as a good starting point for those who want to launch a solochain.
|
|
||||||
//! - [`teyrchain-template`](https://github.com/pezkuwichain/pezkuwi-sdk-teyrchain-template):
|
|
||||||
//! A teyrchain template ready to be connected to a relay-chain, such as [Paseo](https://github.com/paseo-network/.github)
|
|
||||||
//! , Dicle or Pezkuwi.
|
|
||||||
//!
|
|
||||||
//! Note that these templates are mirrored automatically from [this](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/templates)
|
|
||||||
//! directory of pezkuwi-sdk, therefore any changes to them should be made as a PR to this repo.
|
|
||||||
//!
|
|
||||||
//! ## OpenZeppelin
|
|
||||||
//!
|
|
||||||
//! In June 2023, OpenZeppelin was awarded a grant from the Pezkuwi
|
|
||||||
//! treasury for building a number of Pezkuwi-sdk
|
|
||||||
//! based templates. These templates are a great starting point for developers and newcomers.
|
|
||||||
//! So far OpenZeppelin has released two templates, which have been fully [audited](https://github.com/pezkuwichain/polkadot-runtime-templates/tree/main/audits):
|
|
||||||
//! - [`generic-runtime-template`](https://github.com/pezkuwichain/polkadot-runtime-templates?tab=readme-ov-file#generic-runtime-template):
|
|
||||||
//! A minimal template that has all the common pallets that teyrchains use with secure defaults.
|
|
||||||
//! - [`evm-runtime-template`](https://github.com/pezkuwichain/polkadot-runtime-templates/tree/main?tab=readme-ov-file#evm-template):
|
|
||||||
//! This template has EVM compatibility out of the box and allows migrating your solidity contracts
|
|
||||||
//! or EVM compatible dapps easily. It also uses 20 byte addresses like Ethereum and has some
|
|
||||||
//! Account Abstraction support.
|
|
||||||
//!
|
|
||||||
//! ## POP-CLI
|
|
||||||
//!
|
|
||||||
//! Is a CLI tool capable of scaffolding a new pezkuwi-sdk-based project, possibly removing the
|
|
||||||
//! need for templates.
|
|
||||||
//!
|
|
||||||
//! - <https://pop.r0gue.io/cli/>
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
//! # XCM
|
|
||||||
//!
|
|
||||||
//! XCM, or Cross-Consensus Messaging, is a **language** to communicate **intentions** between
|
|
||||||
//! **consensus systems**.
|
|
||||||
//!
|
|
||||||
//! ## Overview
|
|
||||||
//!
|
|
||||||
//! XCM is a standard, specification of which lives in the [xcm format repo](https://github.com/polkadot-fellows/xcm-format).
|
|
||||||
//! It's agnostic both in programming language and blockchain platform, which means it could be used
|
|
||||||
//! in Rust in Pezkuwi, or in Go or C++ in any other platform like Cosmos or Ethereum.
|
|
||||||
//!
|
|
||||||
//! It enables different consensus systems to communicate with each other in an expressive manner.
|
|
||||||
//! Consensus systems include blockchains, smart contracts, and any other state machine that
|
|
||||||
//! achieves consensus in some way.
|
|
||||||
//!
|
|
||||||
//! XCM is executed on a virtual machine called the XCVM.
|
|
||||||
//! Scripts can be written with the XCM language, which are often called XCMs, messages or XCM
|
|
||||||
//! programs. Each program is a series of instructions, which get executed one after the other by
|
|
||||||
//! the virtual machine. These instructions aim to encompass all major things users typically do in
|
|
||||||
//! consensus systems. There are instructions on asset transferring, teleporting, locking, among
|
|
||||||
//! others. New instructions are added and changes to the XCVM are made via the [RFC process](https://github.com/paritytech/xcm-format/blob/master/proposals/0032-process.md).
|
|
||||||
//!
|
|
||||||
//! ## In Pezkuwi SDK
|
|
||||||
//!
|
|
||||||
//! The Pezkuwi SDK allows for easily deploying sovereign blockchains from scratch, all very
|
|
||||||
//! customizable. Dealing with many heterogeneous blockchains can be cumbersome.
|
|
||||||
//! XCM allows all these blockchains to communicate with an agreed-upon language.
|
|
||||||
//! As long as an implementation of the XCVM is implemented, the same XCM program can be executed in
|
|
||||||
//! all blockchains and perform the same task.
|
|
||||||
//!
|
|
||||||
//! ## Implementation
|
|
||||||
//!
|
|
||||||
//! A ready-to-use Rust implementation lives in the [pezkuwi-sdk repo](https://github.com/pezkuwichain/pezkuwi-sdk/tree/main/pezkuwi/xcm),
|
|
||||||
//! but will be moved to its own repo in the future.
|
|
||||||
//!
|
|
||||||
//! Its main components are:
|
|
||||||
//! - [`xcm`](::xcm): The definition of the basic types and instructions.
|
|
||||||
//! - [`xcm_executor`]: An implementation of the virtual machine to execute instructions.
|
|
||||||
//! - [`pezpallet_xcm`]: A FRAME pezpallet for interacting with the executor.
|
|
||||||
//! - [`xcm_builder`]: A collection of types to configure the executor.
|
|
||||||
//! - [`xcm_pez_simulator`]: A playground for trying out different XCM programs and executor
|
|
||||||
//! configurations.
|
|
||||||
//!
|
|
||||||
//! ## Example
|
|
||||||
//!
|
|
||||||
//! To perform the very usual operation of transferring assets, the following XCM program can be
|
|
||||||
//! used:
|
|
||||||
#![doc = docify::embed!("src/pezkuwi_sdk/xcm.rs", example_transfer)]
|
|
||||||
//!
|
|
||||||
//! ## Get started
|
|
||||||
//!
|
|
||||||
//! To learn how it works and to get started, go to the [XCM docs](xcm_pez_docs).
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use xcm::latest::prelude::*;
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[test]
|
|
||||||
fn example_transfer() {
|
|
||||||
let _transfer_program = Xcm::<()>(vec![
|
|
||||||
WithdrawAsset((Here, 100u128).into()),
|
|
||||||
BuyExecution { fees: (Here, 100u128).into(), weight_limit: Unlimited },
|
|
||||||
DepositAsset {
|
|
||||||
assets: All.into(),
|
|
||||||
beneficiary: AccountId32 { id: [0u8; 32].into(), network: None }.into(),
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
//! # State Transition Function
|
|
||||||
//!
|
|
||||||
//! This document briefly explains how in the context of Bizinikiwi-based blockchains, we view the
|
|
||||||
//! blockchain as a **decentralized state transition function**.
|
|
||||||
//!
|
|
||||||
//! Recall that a blockchain's main purpose is to help a permissionless set of entities to agree on
|
|
||||||
//! a shared data-set, and how it evolves. This is called the **State**, also referred to as
|
|
||||||
//! "onchain" data, or *Storage* in the context of FRAME. The state is where the account balance of
|
|
||||||
//! each user is, for example, stored, and there is a canonical version of it that everyone agrees
|
|
||||||
//! upon.
|
|
||||||
//!
|
|
||||||
//! Then, recall that a typical blockchain system will alter its state through execution of blocks.
|
|
||||||
//! *The component that dictates how this state alteration can happen is called the state transition
|
|
||||||
//! function*.
|
|
||||||
#![doc = simple_mermaid::mermaid!("../../../mermaid/stf_simple.mmd")]
|
|
||||||
//!
|
|
||||||
//! In Bizinikiwi-based blockchains, the state transition function is called the *Runtime*. This is
|
|
||||||
//! explained further in [`crate::reference_docs::wasm_meta_protocol`].
|
|
||||||
//!
|
|
||||||
//! With this in mind, we can paint a complete picture of a blockchain as a state machine:
|
|
||||||
#![doc = simple_mermaid::mermaid!("../../../mermaid/stf.mmd")]
|
|
||||||
//!
|
|
||||||
//! In essence, the state of the blockchain at block N is the outcome of applying the state
|
|
||||||
//! transition function to the previous state, and the current block as input. This can be
|
|
||||||
//! mathematically represented as:
|
|
||||||
//!
|
|
||||||
//! ```math
|
|
||||||
//! STF = F(State_N, Block_N) -> State_{N+1}
|
|
||||||
//! ```
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
//! # What is a chain specification
|
|
||||||
//!
|
|
||||||
//! A chain specification file defines the set of properties that are required to run the node as
|
|
||||||
//! part of the chain. The chain specification consists of two main parts:
|
|
||||||
//! - initial state of the runtime,
|
|
||||||
//! - network / logical properties of the chain, the most important property being the list of
|
|
||||||
//! bootnodes.
|
|
||||||
//!
|
|
||||||
//! This document describes how the initial state is handled in pallets and runtime, and how to
|
|
||||||
//! interact with the runtime in order to build the genesis state.
|
|
||||||
//!
|
|
||||||
//! For more information on chain specification and its properties, refer to
|
|
||||||
//! [`pezsc_chain_spec#from-initial-state-to-raw-genesis`].
|
|
||||||
//!
|
|
||||||
//! The initial genesis state can be provided in the following formats:
|
|
||||||
//! - full
|
|
||||||
//! - patch
|
|
||||||
//! - raw
|
|
||||||
//!
|
|
||||||
//! Each of the formats is explained in
|
|
||||||
//! [_chain-spec-format_][`pezsc_chain_spec#chain-spec-formats`].
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//! # `GenesisConfig` for `pezpallet`
|
|
||||||
//!
|
|
||||||
//! Every frame pezpallet may have its initial state which is defined by the `GenesisConfig`
|
|
||||||
//! internal struct. It is a regular Rust struct, annotated with the [`pezpallet::genesis_config`]
|
|
||||||
//! attribute.
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pezpallet_bar_GenesisConfig)]
|
|
||||||
//!
|
|
||||||
//! The struct shall be defined within the pezpallet `mod`, as in the following code:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pezpallet_bar)]
|
|
||||||
//!
|
|
||||||
//! The initial state conveyed in the `GenesisConfig` struct is transformed into state storage
|
|
||||||
//! items by means of the [`BuildGenesisConfig`] trait, which shall be implemented for the
|
|
||||||
//! pezpallet's `GenesisConfig` struct. The [`pezpallet::genesis_build`] attribute shall be attached
|
|
||||||
//! to the `impl` block:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pezpallet_bar_build)]
|
|
||||||
//!
|
|
||||||
//! `GenesisConfig` may also contain more complicated types, including nested structs or enums, as
|
|
||||||
//! in the example for `pezpallet_foo`:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pezpallet_foo_GenesisConfig)]
|
|
||||||
//!
|
|
||||||
//! Note that [`serde`] attributes can be used to control how the data
|
|
||||||
//! structures are stored into JSON. In the following example, the [`pezsp_core::bytes`] function is
|
|
||||||
//! used to serialize the `values` field.
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", SomeFooData2)]
|
|
||||||
//!
|
|
||||||
//! Please note that fields of `GenesisConfig` may not be directly mapped to storage items. In the
|
|
||||||
//! following example, the initial struct fields are used to compute (sum) the value that will be
|
|
||||||
//! stored in the state as `ProcessedEnumValue`:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pezpallet_foo_build)]
|
|
||||||
//!
|
|
||||||
//! # `GenesisConfig` for `runtimes`
|
|
||||||
//!
|
|
||||||
//! The runtime genesis config struct consists of configs for every pezpallet. For the
|
|
||||||
//! [_demonstration runtime_][`pez_chain_spec_guide_runtime`] used in this guide, it consists of
|
|
||||||
//! `SystemConfig`, `BarConfig`, and `FooConfig`. This structure was automatically generated by a
|
|
||||||
//! macro and it can be sneak-peeked here: [`RuntimeGenesisConfig`]. For further reading on
|
|
||||||
//! generated runtime types, refer to [`frame_runtime_types`].
|
|
||||||
//!
|
|
||||||
//! The macro automatically adds an attribute that renames all the fields to [`camelCase`]. It is a
|
|
||||||
//! good practice to add it to nested structures too, to have the naming of the JSON keys consistent
|
|
||||||
//! across the chain-spec file.
|
|
||||||
//!
|
|
||||||
//! ## `Default` for `GenesisConfig`
|
|
||||||
//!
|
|
||||||
//! `GenesisConfig` of all pallets must implement the `Default` trait. These are aggregated into
|
|
||||||
//! the runtime's `RuntimeGenesisConfig`'s `Default`.
|
|
||||||
//!
|
|
||||||
//! The default value of `RuntimeGenesisConfig` can be queried by the [`GenesisBuilder::get_preset`]
|
|
||||||
//! function provided by the runtime with `id:None`.
|
|
||||||
//!
|
|
||||||
//! A default value for `RuntimeGenesisConfig` usually is not operational. This is because for some
|
|
||||||
//! pallets it is not possible to define good defaults (e.g. an initial set of authorities).
|
|
||||||
//!
|
|
||||||
//! A default value is a base upon which a patch for `GenesisConfig` is applied. A good description
|
|
||||||
//! of how it exactly works is provided in [`get_storage_for_patch`] (and also in
|
|
||||||
//! [`GenesisBuilder::get_preset`]). A patch can be provided as an external file (manually created)
|
|
||||||
//! or as a built-in runtime preset. More info on presets is in the material to follow.
|
|
||||||
//!
|
|
||||||
//! ## Implementing `GenesisBuilder` for runtime
|
|
||||||
//!
|
|
||||||
//! The runtime exposes a dedicated runtime API for interacting with its genesis config:
|
|
||||||
//! [`pezsp_genesis_builder::GenesisBuilder`]. The implementation shall be provided within
|
|
||||||
//! the [`pezsp_api::impl_runtime_apis`] macro, typically making use of some helpers provided:
|
|
||||||
//! [`build_state`], [`get_preset`].
|
|
||||||
//! A typical implementation of [`pezsp_genesis_builder::GenesisBuilder`] looks as follows:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/runtime.rs", runtime_impl)]
|
|
||||||
//!
|
|
||||||
//! Please note that two functions are customized: `preset_names` and `get_preset`. The first one
|
|
||||||
//! just provides a `Vec` of the names of supported presets, while the latter delegates the call
|
|
||||||
//! to a function that maps the name to an actual preset:
|
|
||||||
//! [`pez_chain_spec_guide_runtime::presets::get_builtin_preset`]
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", get_builtin_preset)]
|
|
||||||
//!
|
|
||||||
//! ## Genesis state presets for runtime
|
|
||||||
//!
|
|
||||||
//! The runtime may provide many flavors of initial genesis state. This may be useful for predefined
|
|
||||||
//! testing networks, local development, or CI integration tests. Predefined genesis state may
|
|
||||||
//! contain a list of pre-funded accounts, predefined authorities for consensus, sudo key, and many
|
|
||||||
//! others useful for testing.
|
|
||||||
//!
|
|
||||||
//! Internally, presets can be provided in a number of ways:
|
|
||||||
//! - using [`build_struct_json_patch`] macro (**recommended**):
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_2)]
|
|
||||||
//! - JSON using runtime types to serialize values:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_3)]
|
|
||||||
//! - JSON in string form:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_1)]
|
|
||||||
//!
|
|
||||||
//! It is worth noting that a preset does not have to be the full `RuntimeGenesisConfig`, in that
|
|
||||||
//! sense that it does not have to contain all the keys of the struct. The preset is actually a JSON
|
|
||||||
//! patch that will be merged with the default value of `RuntimeGenesisConfig`. This approach should
|
|
||||||
//! simplify maintenance of built-in presets. The following example illustrates a runtime genesis
|
|
||||||
//! config patch with a single key built using [`build_struct_json_patch`] macro:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_4)]
|
|
||||||
//! This results in the following JSON blob:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", preset_4_json)]
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//! ## Note on the importance of testing presets
|
|
||||||
//!
|
|
||||||
//! It is recommended to always test presets by adding tests that convert the preset into the
|
|
||||||
//! raw storage. Converting to raw storage involves the deserialization of the provided JSON blob,
|
|
||||||
//! which enforces the verification of the preset. The following code shows one of the approaches
|
|
||||||
//! that can be taken for testing:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", check_presets)]
|
|
||||||
//!
|
|
||||||
//! ## Note on the importance of using the `deny_unknown_fields` attribute
|
|
||||||
//!
|
|
||||||
//! It is worth noting that when manually building preset JSON blobs it is easy to make a
|
|
||||||
//! hard-to-spot mistake, as in the following example ([`FooStruct`] does not contain `fieldC`):
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_invalid)]
|
|
||||||
//! Even though `preset_invalid` contains a key that does not exist, the deserialization of the JSON
|
|
||||||
//! blob does not fail. The misspelling is silently ignored due to the lack of the
|
|
||||||
//! [`deny_unknown_fields`] attribute on the [`FooStruct`] struct, which is internally used in
|
|
||||||
//! `GenesisConfig`.
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", invalid_preset_works)]
|
|
||||||
//!
|
|
||||||
//! To avoid this problem [`build_struct_json_patch`] macro shall be used whenever possible (it
|
|
||||||
//! internally instantiates the struct before serializang it JSON blob, so all unknown fields shall
|
|
||||||
//! be caught at compilation time).
|
|
||||||
//!
|
|
||||||
//! ## Runtime `GenesisConfig` raw format
|
|
||||||
//!
|
|
||||||
//! A raw format of genesis config contains just the state's keys and values as they are stored in
|
|
||||||
//! the storage. This format is used to directly initialize the genesis storage. This format is
|
|
||||||
//! useful for long-term running chains, where the `GenesisConfig` structure for pallets may be
|
|
||||||
//! evolving over time. The JSON representation created at some point in time may no longer be
|
|
||||||
//! deserializable in the future, making a chain specification useless. The raw format is
|
|
||||||
//! recommended for production chains.
|
|
||||||
//!
|
|
||||||
//! For a detailed description of how the raw format is built, please refer to
|
|
||||||
//! [_chain-spec-raw-genesis_][`pezsc_chain_spec#from-initial-state-to-raw-genesis`]. Plain and
|
|
||||||
//! corresponding raw examples of chain-spec are given in
|
|
||||||
//! [_chain-spec-examples_][`pezsc_chain_spec#json-chain-specification-example`].
|
|
||||||
//! The [`chain_spec_builder`] util supports building the raw storage.
|
|
||||||
//!
|
|
||||||
//! # Interacting with the tool
|
|
||||||
//!
|
|
||||||
//! The [`chain_spec_builder`] util allows interaction with the runtime in order to list or display
|
|
||||||
//! presets and build the chain specification file. It is possible to use the tool with the
|
|
||||||
//! [_demonstration runtime_][`pez_chain_spec_guide_runtime`]. To build the required packages, just
|
|
||||||
//! run the following command:
|
|
||||||
//!
|
|
||||||
//! ```ignore
|
|
||||||
//! cargo build -p pezstaging-chain-spec-builder -p pez-chain-spec-guide-runtime --release
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! The `chain-spec-builder` util can also be installed with `cargo install`:
|
|
||||||
//!
|
|
||||||
//! ```ignore
|
|
||||||
//! cargo install pezstaging-chain-spec-builder
|
|
||||||
//! cargo build -p pez-chain-spec-guide-runtime --release
|
|
||||||
//! ```
|
|
||||||
//! Here are some examples in the form of rust tests:
|
|
||||||
//! ## Listing available preset names:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", cmd_list_presets)]
|
|
||||||
//! ## Displaying preset with given name
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", cmd_get_preset)]
|
|
||||||
//! ## Building a solo chain-spec (the default) using given preset
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", cmd_generate_chain_spec)]
|
|
||||||
//! ## Building a teyrchain chain-spec using given preset
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", cmd_generate_para_chain_spec)]
|
|
||||||
//!
|
|
||||||
//! [`RuntimeGenesisConfig`]:
|
|
||||||
//! pez_chain_spec_guide_runtime::runtime::RuntimeGenesisConfig
|
|
||||||
//! [`FooStruct`]:
|
|
||||||
//! pez_chain_spec_guide_runtime::pallets::FooStruct
|
|
||||||
//! [`impl_runtime_apis`]: pezframe::runtime::prelude::impl_runtime_apis
|
|
||||||
//! [`build_state`]: pezframe_support::genesis_builder_helper::build_state
|
|
||||||
//! [`get_preset`]: pezframe_support::genesis_builder_helper::get_preset
|
|
||||||
//! [`pezpallet::genesis_build`]: pezframe_support::pezpallet_macros::genesis_build
|
|
||||||
//! [`pezpallet::genesis_config`]: pezframe_support::pezpallet_macros::genesis_config
|
|
||||||
//! [`build_struct_json_patch`]: pezframe_support::build_struct_json_patch
|
|
||||||
//! [`BuildGenesisConfig`]: pezframe_support::traits::BuildGenesisConfig
|
|
||||||
//! [`serde`]: https://serde.rs/field-attrs.html
|
|
||||||
//! [`get_storage_for_patch`]: pezsc_chain_spec::GenesisConfigBuilderRuntimeCaller::get_storage_for_patch
|
|
||||||
//! [`GenesisBuilder::get_preset`]: pezsp_genesis_builder::GenesisBuilder::get_preset
|
|
||||||
//! [`deny_unknown_fields`]: https://serde.rs/container-attrs.html#deny_unknown_fields
|
|
||||||
//! [`camelCase`]: https://serde.rs/container-attrs.html#rename_all
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "pez-chain-spec-guide-runtime"
|
|
||||||
description = "A minimal runtime for chain spec guide"
|
|
||||||
version = "0.0.0"
|
|
||||||
license = "MIT-0"
|
|
||||||
authors.workspace = true
|
|
||||||
homepage.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
publish = false
|
|
||||||
documentation.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
codec = { workspace = true }
|
|
||||||
docify = { workspace = true }
|
|
||||||
pezframe-support = { workspace = true }
|
|
||||||
pezframe-system = { workspace = true }
|
|
||||||
scale-info = { workspace = true }
|
|
||||||
serde = { workspace = true }
|
|
||||||
serde_json = { workspace = true }
|
|
||||||
|
|
||||||
# this is a frame-based runtime, thus importing `frame` with runtime feature enabled.
|
|
||||||
pezframe = { features = ["experimental", "runtime"], workspace = true }
|
|
||||||
|
|
||||||
# genesis builder that allows us to interact with runtime genesis config
|
|
||||||
pezsp-api = { workspace = true }
|
|
||||||
pezsp-application-crypto = { features = ["serde"], workspace = true }
|
|
||||||
pezsp-core = { workspace = true }
|
|
||||||
pezsp-genesis-builder = { workspace = true }
|
|
||||||
pezsp-keyring = { workspace = true }
|
|
||||||
pezsp-runtime = { features = ["serde"], workspace = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
cmd_lib = { workspace = true }
|
|
||||||
pezsc-chain-spec = { workspace = true, default-features = true }
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
bizinikiwi-wasm-builder = { optional = true, workspace = true, default-features = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["std"]
|
|
||||||
std = [
|
|
||||||
"bizinikiwi-wasm-builder",
|
|
||||||
"bizinikiwi-wasm-builder?/std",
|
|
||||||
"codec/std",
|
|
||||||
"pezframe-support/std",
|
|
||||||
"pezframe-system/std",
|
|
||||||
"pezframe/std",
|
|
||||||
"pezsc-chain-spec/std",
|
|
||||||
"pezsp-api/std",
|
|
||||||
"pezsp-application-crypto/std",
|
|
||||||
"pezsp-core/std",
|
|
||||||
"pezsp-genesis-builder/std",
|
|
||||||
"pezsp-keyring/std",
|
|
||||||
"pezsp-runtime/std",
|
|
||||||
"scale-info/std",
|
|
||||||
"serde/std",
|
|
||||||
"serde_json/std",
|
|
||||||
]
|
|
||||||
runtime-benchmarks = [
|
|
||||||
"bizinikiwi-wasm-builder?/runtime-benchmarks",
|
|
||||||
"pezframe-support/runtime-benchmarks",
|
|
||||||
"pezframe-system/runtime-benchmarks",
|
|
||||||
"pezframe/runtime-benchmarks",
|
|
||||||
"pezsc-chain-spec/runtime-benchmarks",
|
|
||||||
"pezsp-api/runtime-benchmarks",
|
|
||||||
"pezsp-application-crypto/runtime-benchmarks",
|
|
||||||
"pezsp-genesis-builder/runtime-benchmarks",
|
|
||||||
"pezsp-keyring/runtime-benchmarks",
|
|
||||||
"pezsp-runtime/runtime-benchmarks",
|
|
||||||
]
|
|
||||||
try-runtime = [
|
|
||||||
"pezframe-support/try-runtime",
|
|
||||||
"pezframe-system/try-runtime",
|
|
||||||
"pezframe/try-runtime",
|
|
||||||
"pezsc-chain-spec/try-runtime",
|
|
||||||
"pezsp-api/try-runtime",
|
|
||||||
"pezsp-genesis-builder/try-runtime",
|
|
||||||
"pezsp-keyring/try-runtime",
|
|
||||||
"pezsp-runtime/try-runtime",
|
|
||||||
]
|
|
||||||
serde = []
|
|
||||||
experimental = []
|
|
||||||
tuples-96 = []
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
// This file is part of Bizinikiwi.
|
|
||||||
|
|
||||||
// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
{
|
|
||||||
bizinikiwi_wasm_builder::WasmBuilder::build_using_defaults();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
// This file is part of Bizinikiwi.
|
|
||||||
|
|
||||||
// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
|
||||||
|
|
||||||
//! A minimal runtime that shows runtime genesis state.
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
pub mod pallets;
|
|
||||||
pub mod presets;
|
|
||||||
pub mod runtime;
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub use runtime::{WASM_BINARY, WASM_BINARY_BLOATY};
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
// This file is part of Bizinikiwi.
|
|
||||||
|
|
||||||
// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
//! Pallets for the chain-spec demo runtime.
|
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use pezframe::prelude::*;
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[pezframe::pezpallet(dev_mode)]
|
|
||||||
pub mod pezpallet_bar {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[pezpallet::config]
|
|
||||||
pub trait Config: pezframe_system::Config {}
|
|
||||||
|
|
||||||
#[pezpallet::pezpallet]
|
|
||||||
pub struct Pezpallet<T>(_);
|
|
||||||
|
|
||||||
#[pezpallet::storage]
|
|
||||||
pub(super) type InitialAccount<T: Config> = StorageValue<Value = T::AccountId>;
|
|
||||||
|
|
||||||
/// Simple `GenesisConfig`.
|
|
||||||
#[pezpallet::genesis_config]
|
|
||||||
#[derive(DefaultNoBound)]
|
|
||||||
#[docify::export(pezpallet_bar_GenesisConfig)]
|
|
||||||
pub struct GenesisConfig<T: Config> {
|
|
||||||
pub initial_account: Option<T::AccountId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pezpallet::genesis_build]
|
|
||||||
#[docify::export(pezpallet_bar_build)]
|
|
||||||
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
|
|
||||||
/// The storage building function that presents a direct mapping of the initial config
|
|
||||||
/// values to the storage items.
|
|
||||||
fn build(&self) {
|
|
||||||
InitialAccount::<T>::set(self.initial_account.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The sample structure used in `GenesisConfig`.
|
|
||||||
///
|
|
||||||
/// This structure does not deny unknown fields. This may lead to some problems.
|
|
||||||
#[derive(Default, serde::Serialize, serde::Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct FooStruct {
|
|
||||||
pub field_a: u8,
|
|
||||||
pub field_b: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The sample structure used in `GenesisConfig`.
|
|
||||||
///
|
|
||||||
/// This structure does not deny unknown fields. This may lead to some problems.
|
|
||||||
#[derive(Default, serde::Serialize, serde::Deserialize)]
|
|
||||||
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
|
||||||
pub struct SomeFooData1 {
|
|
||||||
pub a: u8,
|
|
||||||
pub b: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Another sample structure used in `GenesisConfig`.
|
|
||||||
///
|
|
||||||
/// The user defined serialization is used.
|
|
||||||
#[derive(Default, serde::Serialize, serde::Deserialize)]
|
|
||||||
#[docify::export]
|
|
||||||
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
|
||||||
pub struct SomeFooData2 {
|
|
||||||
#[serde(default, with = "pezsp_core::bytes")]
|
|
||||||
pub values: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sample enum used in `GenesisConfig`.
|
|
||||||
#[derive(Default, serde::Serialize, serde::Deserialize)]
|
|
||||||
pub enum FooEnum {
|
|
||||||
#[default]
|
|
||||||
Data0,
|
|
||||||
Data1(SomeFooData1),
|
|
||||||
Data2(SomeFooData2),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[pezframe::pezpallet(dev_mode)]
|
|
||||||
pub mod pezpallet_foo {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[pezpallet::config]
|
|
||||||
pub trait Config: pezframe_system::Config {}
|
|
||||||
|
|
||||||
#[pezpallet::pezpallet]
|
|
||||||
pub struct Pezpallet<T>(_);
|
|
||||||
|
|
||||||
#[pezpallet::storage]
|
|
||||||
pub type ProcessedEnumValue<T> = StorageValue<Value = u64>;
|
|
||||||
#[pezpallet::storage]
|
|
||||||
pub type SomeInteger<T> = StorageValue<Value = u32>;
|
|
||||||
|
|
||||||
/// The more sophisticated structure for conveying initial state.
|
|
||||||
#[docify::export(pezpallet_foo_GenesisConfig)]
|
|
||||||
#[pezpallet::genesis_config]
|
|
||||||
#[derive(DefaultNoBound)]
|
|
||||||
pub struct GenesisConfig<T: Config> {
|
|
||||||
pub some_integer: u32,
|
|
||||||
pub some_enum: FooEnum,
|
|
||||||
pub some_struct: FooStruct,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub _phantom: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pezpallet::genesis_build]
|
|
||||||
#[docify::export(pezpallet_foo_build)]
|
|
||||||
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
|
|
||||||
/// The build method that indirectly maps an initial config values into the storage items.
|
|
||||||
fn build(&self) {
|
|
||||||
let processed_value: u64 = match &self.some_enum {
|
|
||||||
FooEnum::Data0 => 0,
|
|
||||||
FooEnum::Data1(v) => (v.a + v.b).into(),
|
|
||||||
FooEnum::Data2(v) => v.values.iter().map(|v| *v as u64).sum(),
|
|
||||||
};
|
|
||||||
ProcessedEnumValue::<T>::set(Some(processed_value));
|
|
||||||
SomeInteger::<T>::set(Some(self.some_integer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
// This file is part of Bizinikiwi.
|
|
||||||
|
|
||||||
// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
//! Presets for the chain-spec demo runtime.
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
pallets::{FooEnum, SomeFooData1, SomeFooData2},
|
|
||||||
runtime::{BarConfig, FooConfig, RuntimeGenesisConfig},
|
|
||||||
};
|
|
||||||
use alloc::vec;
|
|
||||||
use pezframe_support::build_struct_json_patch;
|
|
||||||
use pezsp_application_crypto::Ss58Codec;
|
|
||||||
use pezsp_keyring::Sr25519Keyring;
|
|
||||||
use serde_json::{json, to_string, Value};
|
|
||||||
|
|
||||||
/// A demo preset with strings only.
|
|
||||||
pub const PRESET_1: &str = "preset_1";
|
|
||||||
/// A demo preset with real types.
|
|
||||||
pub const PRESET_2: &str = "preset_2";
|
|
||||||
/// Another demo preset with real types and manually created json object.
|
|
||||||
pub const PRESET_3: &str = "preset_3";
|
|
||||||
/// A single value patch preset.
|
|
||||||
pub const PRESET_4: &str = "preset_4";
|
|
||||||
/// A single value patch preset.
|
|
||||||
pub const PRESET_INVALID: &str = "preset_invalid";
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
/// Function provides a preset demonstrating how use string representation of preset's internal
|
|
||||||
/// values.
|
|
||||||
fn preset_1() -> Value {
|
|
||||||
json!({
|
|
||||||
"bar": {
|
|
||||||
"initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL",
|
|
||||||
},
|
|
||||||
"foo": {
|
|
||||||
"someEnum": {
|
|
||||||
"Data2": {
|
|
||||||
"values": "0x0c0f"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"someStruct" : {
|
|
||||||
"fieldA": 10,
|
|
||||||
"fieldB": 20
|
|
||||||
},
|
|
||||||
"someInteger": 100
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
/// Function provides a preset demonstrating how to create a preset using
|
|
||||||
/// [`build_struct_json_patch`] macro.
|
|
||||||
fn preset_2() -> Value {
|
|
||||||
build_struct_json_patch!(RuntimeGenesisConfig {
|
|
||||||
foo: FooConfig {
|
|
||||||
some_integer: 200,
|
|
||||||
some_enum: FooEnum::Data2(SomeFooData2 { values: vec![0x0c, 0x10] })
|
|
||||||
},
|
|
||||||
bar: BarConfig { initial_account: Some(Sr25519Keyring::Ferdie.public().into()) },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
/// Function provides a preset demonstrating how use the actual types to manually create a JSON
|
|
||||||
/// representing the preset.
|
|
||||||
fn preset_3() -> Value {
|
|
||||||
json!({
|
|
||||||
"bar": {
|
|
||||||
"initialAccount": Sr25519Keyring::Alice.public().to_ss58check(),
|
|
||||||
},
|
|
||||||
"foo": {
|
|
||||||
"someEnum": FooEnum::Data1(
|
|
||||||
SomeFooData1 {
|
|
||||||
a: 12,
|
|
||||||
b: 16
|
|
||||||
}
|
|
||||||
),
|
|
||||||
"someInteger": 300
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
/// Function provides a minimal preset demonstrating how to patch single key in
|
|
||||||
/// `RuntimeGenesisConfig` using [`build_struct_json_patch`] macro.
|
|
||||||
pub fn preset_4() -> Value {
|
|
||||||
build_struct_json_patch!(RuntimeGenesisConfig {
|
|
||||||
foo: FooConfig { some_enum: FooEnum::Data2(SomeFooData2 { values: vec![0x0c, 0x10] }) },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
/// Function provides an invalid preset demonstrating how important is use of
|
|
||||||
/// `deny_unknown_fields` in data structures used in `GenesisConfig`.
|
|
||||||
fn preset_invalid() -> Value {
|
|
||||||
json!({
|
|
||||||
"foo": {
|
|
||||||
"someStruct": {
|
|
||||||
"fieldC": 5
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Provides a JSON representation of preset identified by given `id`.
|
|
||||||
///
|
|
||||||
/// If no preset with given `id` exits `None` is returned.
|
|
||||||
#[docify::export]
|
|
||||||
pub fn get_builtin_preset(id: &pezsp_genesis_builder::PresetId) -> Option<alloc::vec::Vec<u8>> {
|
|
||||||
let preset = match id.as_ref() {
|
|
||||||
PRESET_1 => preset_1(),
|
|
||||||
PRESET_2 => preset_2(),
|
|
||||||
PRESET_3 => preset_3(),
|
|
||||||
PRESET_4 => preset_4(),
|
|
||||||
PRESET_INVALID => preset_invalid(),
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(
|
|
||||||
to_string(&preset)
|
|
||||||
.expect("serialization to json is expected to work. qed.")
|
|
||||||
.into_bytes(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[docify::export]
|
|
||||||
fn check_presets() {
|
|
||||||
let builder = pezsc_chain_spec::GenesisConfigBuilderRuntimeCaller::<()>::new(
|
|
||||||
crate::WASM_BINARY.expect("wasm binary shall exists"),
|
|
||||||
);
|
|
||||||
assert!(builder.get_storage_for_named_preset(Some(&PRESET_1.to_string())).is_ok());
|
|
||||||
assert!(builder.get_storage_for_named_preset(Some(&PRESET_2.to_string())).is_ok());
|
|
||||||
assert!(builder.get_storage_for_named_preset(Some(&PRESET_3.to_string())).is_ok());
|
|
||||||
assert!(builder.get_storage_for_named_preset(Some(&PRESET_4.to_string())).is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[docify::export]
|
|
||||||
fn invalid_preset_works() {
|
|
||||||
let builder = pezsc_chain_spec::GenesisConfigBuilderRuntimeCaller::<()>::new(
|
|
||||||
crate::WASM_BINARY.expect("wasm binary shall exists"),
|
|
||||||
);
|
|
||||||
// Even though a preset contains invalid_key, conversion to raw storage does not fail. This is
|
|
||||||
// because the [`FooStruct`] structure is not annotated with `deny_unknown_fields` [`serde`]
|
|
||||||
// attribute.
|
|
||||||
// This may lead to hard to debug problems, that's why using ['deny_unknown_fields'] is
|
|
||||||
// recommended.
|
|
||||||
assert!(builder.get_storage_for_named_preset(Some(&PRESET_INVALID.to_string())).is_ok());
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
// This file is part of Bizinikiwi.
|
|
||||||
|
|
||||||
// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
//! A minimal runtime that shows runtime genesis state.
|
|
||||||
|
|
||||||
// Make the WASM binary available.
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
pallets::{pezpallet_bar, pezpallet_foo},
|
|
||||||
presets::*,
|
|
||||||
};
|
|
||||||
use alloc::{vec, vec::Vec};
|
|
||||||
use pezframe::{
|
|
||||||
deps::pezframe_support::genesis_builder_helper::{build_state, get_preset},
|
|
||||||
prelude::*,
|
|
||||||
runtime::prelude::*,
|
|
||||||
};
|
|
||||||
use pezsp_api::impl_runtime_apis;
|
|
||||||
use pezsp_genesis_builder::PresetId;
|
|
||||||
use pezsp_runtime::traits::Block as BlockT;
|
|
||||||
|
|
||||||
/// The runtime version.
|
|
||||||
#[runtime_version]
|
|
||||||
pub const VERSION: RuntimeVersion = RuntimeVersion {
|
|
||||||
spec_name: alloc::borrow::Cow::Borrowed("pez-minimal-template-runtime"),
|
|
||||||
impl_name: alloc::borrow::Cow::Borrowed("pez-minimal-template-runtime"),
|
|
||||||
authoring_version: 1,
|
|
||||||
spec_version: 0,
|
|
||||||
impl_version: 1,
|
|
||||||
apis: RUNTIME_API_VERSIONS,
|
|
||||||
transaction_version: 1,
|
|
||||||
system_version: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The signed extensions that are added to the runtime.
|
|
||||||
type SignedExtra = ();
|
|
||||||
|
|
||||||
// Composes the runtime by adding all the used pallets and deriving necessary types.
|
|
||||||
#[frame_construct_runtime]
|
|
||||||
mod runtime {
|
|
||||||
/// The main runtime type.
|
|
||||||
#[runtime::runtime]
|
|
||||||
#[runtime::derive(
|
|
||||||
RuntimeCall,
|
|
||||||
RuntimeEvent,
|
|
||||||
RuntimeError,
|
|
||||||
RuntimeOrigin,
|
|
||||||
RuntimeTask,
|
|
||||||
RuntimeViewFunction
|
|
||||||
)]
|
|
||||||
pub struct Runtime;
|
|
||||||
|
|
||||||
/// Mandatory system pezpallet that should always be included in a FRAME runtime.
|
|
||||||
#[runtime::pezpallet_index(0)]
|
|
||||||
pub type System = pezframe_system::Pezpallet<Runtime>;
|
|
||||||
|
|
||||||
/// Sample pezpallet 1
|
|
||||||
#[runtime::pezpallet_index(1)]
|
|
||||||
pub type Bar = pezpallet_bar::Pezpallet<Runtime>;
|
|
||||||
|
|
||||||
/// Sample pezpallet 2
|
|
||||||
#[runtime::pezpallet_index(2)]
|
|
||||||
pub type Foo = pezpallet_foo::Pezpallet<Runtime>;
|
|
||||||
}
|
|
||||||
|
|
||||||
parameter_types! {
|
|
||||||
pub const Version: RuntimeVersion = VERSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implements the types required for the system pezpallet.
|
|
||||||
#[derive_impl(pezframe_system::config_preludes::SolochainDefaultConfig)]
|
|
||||||
impl pezframe_system::Config for Runtime {
|
|
||||||
type Block = Block;
|
|
||||||
type Version = Version;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pezpallet_bar::Config for Runtime {}
|
|
||||||
impl pezpallet_foo::Config for Runtime {}
|
|
||||||
|
|
||||||
type Block = pezframe::runtime::types_common::BlockOf<Runtime, SignedExtra>;
|
|
||||||
type _Header = HeaderFor<Runtime>;
|
|
||||||
|
|
||||||
#[docify::export(runtime_impl)]
|
|
||||||
impl_runtime_apis! {
|
|
||||||
impl pezsp_genesis_builder::GenesisBuilder<Block> for Runtime {
|
|
||||||
fn build_state(config: Vec<u8>) -> pezsp_genesis_builder::Result {
|
|
||||||
build_state::<RuntimeGenesisConfig>(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_preset(id: &Option<pezsp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
|
|
||||||
get_preset::<RuntimeGenesisConfig>(id, get_builtin_preset)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn preset_names() -> Vec<pezsp_genesis_builder::PresetId> {
|
|
||||||
vec![
|
|
||||||
PresetId::from(PRESET_1),
|
|
||||||
PresetId::from(PRESET_2),
|
|
||||||
PresetId::from(PRESET_3),
|
|
||||||
PresetId::from(PRESET_4),
|
|
||||||
PresetId::from(PRESET_INVALID)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pezsp_api::Core<Block> for Runtime {
|
|
||||||
fn version() -> RuntimeVersion { VERSION }
|
|
||||||
fn execute_block(_: <Block as BlockT>::LazyBlock) { }
|
|
||||||
fn initialize_block(_: &<Block as BlockT>::Header) -> ExtrinsicInclusionMode { ExtrinsicInclusionMode::default() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,203 +0,0 @@
|
|||||||
use cmd_lib::*;
|
|
||||||
use serde_json::{json, Value};
|
|
||||||
use std::str;
|
|
||||||
|
|
||||||
fn wasm_file_path() -> &'static str {
|
|
||||||
pez_chain_spec_guide_runtime::runtime::WASM_BINARY_PATH
|
|
||||||
.expect("pez_chain_spec_guide_runtime wasm should exist. qed")
|
|
||||||
}
|
|
||||||
|
|
||||||
const CHAIN_SPEC_BUILDER_PATH: &str = "../../../../../target/release/chain-spec-builder";
|
|
||||||
|
|
||||||
macro_rules! bash(
|
|
||||||
( chain-spec-builder $($a:tt)* ) => {{
|
|
||||||
let path = get_chain_spec_builder_path();
|
|
||||||
spawn_with_output!(
|
|
||||||
$path $($a)*
|
|
||||||
)
|
|
||||||
.expect("a process running. qed")
|
|
||||||
.wait_with_output()
|
|
||||||
.expect("to get output. qed.")
|
|
||||||
|
|
||||||
}}
|
|
||||||
);
|
|
||||||
|
|
||||||
fn get_chain_spec_builder_path() -> &'static str {
|
|
||||||
run_cmd!(
|
|
||||||
cargo build --release -p pezstaging-chain-spec-builder --bin chain-spec-builder
|
|
||||||
)
|
|
||||||
.expect("Failed to execute command");
|
|
||||||
CHAIN_SPEC_BUILDER_PATH
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export_content]
|
|
||||||
fn cmd_list_presets(runtime_path: &str) -> String {
|
|
||||||
bash!(
|
|
||||||
chain-spec-builder list-presets -r $runtime_path
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn list_presets() {
|
|
||||||
let output: serde_json::Value =
|
|
||||||
serde_json::from_slice(cmd_list_presets(wasm_file_path()).as_bytes()).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
output,
|
|
||||||
json!({
|
|
||||||
"presets":[
|
|
||||||
"preset_1",
|
|
||||||
"preset_2",
|
|
||||||
"preset_3",
|
|
||||||
"preset_4",
|
|
||||||
"preset_invalid"
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
"Output did not match expected"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export_content]
|
|
||||||
fn cmd_get_preset(runtime_path: &str) -> String {
|
|
||||||
bash!(
|
|
||||||
chain-spec-builder display-preset -r $runtime_path -p preset_2
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_preset() {
|
|
||||||
let output: serde_json::Value =
|
|
||||||
serde_json::from_slice(cmd_get_preset(wasm_file_path()).as_bytes()).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
output,
|
|
||||||
json!({
|
|
||||||
"bar": {
|
|
||||||
"initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL",
|
|
||||||
},
|
|
||||||
"foo": {
|
|
||||||
"someEnum": {
|
|
||||||
"Data2": {
|
|
||||||
"values": "0x0c10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"someInteger": 200
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
"Output did not match expected"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export_content]
|
|
||||||
fn cmd_generate_chain_spec(runtime_path: &str) -> String {
|
|
||||||
bash!(
|
|
||||||
chain-spec-builder -c /dev/stdout create -r $runtime_path named-preset preset_2
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn generate_chain_spec() {
|
|
||||||
let mut output: serde_json::Value =
|
|
||||||
serde_json::from_slice(cmd_generate_chain_spec(wasm_file_path()).as_bytes()).unwrap();
|
|
||||||
if let Some(code) = output["genesis"]["runtimeGenesis"].as_object_mut().unwrap().get_mut("code")
|
|
||||||
{
|
|
||||||
*code = Value::String("0x123".to_string());
|
|
||||||
}
|
|
||||||
assert_eq!(
|
|
||||||
output,
|
|
||||||
json!({
|
|
||||||
"name": "Custom",
|
|
||||||
"id": "custom",
|
|
||||||
"chainType": "Live",
|
|
||||||
"bootNodes": [],
|
|
||||||
"telemetryEndpoints": null,
|
|
||||||
"protocolId": null,
|
|
||||||
"properties": { "tokenDecimals": 12, "tokenSymbol": "UNIT" },
|
|
||||||
"codeSubstitutes": {},
|
|
||||||
"genesis": {
|
|
||||||
"runtimeGenesis": {
|
|
||||||
"code": "0x123",
|
|
||||||
"patch": {
|
|
||||||
"bar": {
|
|
||||||
"initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL"
|
|
||||||
},
|
|
||||||
"foo": {
|
|
||||||
"someEnum": {
|
|
||||||
"Data2": {
|
|
||||||
"values": "0x0c10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"someInteger": 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
"Output did not match expected"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export_content]
|
|
||||||
fn cmd_generate_para_chain_spec(runtime_path: &str) -> String {
|
|
||||||
bash!(
|
|
||||||
chain-spec-builder -c /dev/stdout create -c pezkuwi -p 1000 -r $runtime_path named-preset preset_2
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn generate_para_chain_spec() {
|
|
||||||
let mut output: serde_json::Value =
|
|
||||||
serde_json::from_slice(cmd_generate_para_chain_spec(wasm_file_path()).as_bytes()).unwrap();
|
|
||||||
if let Some(code) = output["genesis"]["runtimeGenesis"].as_object_mut().unwrap().get_mut("code")
|
|
||||||
{
|
|
||||||
*code = Value::String("0x123".to_string());
|
|
||||||
}
|
|
||||||
assert_eq!(
|
|
||||||
output,
|
|
||||||
json!({
|
|
||||||
"name": "Custom",
|
|
||||||
"id": "custom",
|
|
||||||
"chainType": "Live",
|
|
||||||
"bootNodes": [],
|
|
||||||
"telemetryEndpoints": null,
|
|
||||||
"protocolId": null,
|
|
||||||
"relay_chain": "pezkuwi",
|
|
||||||
"para_id": 1000,
|
|
||||||
"properties": { "tokenDecimals": 12, "tokenSymbol": "UNIT" },
|
|
||||||
"codeSubstitutes": {},
|
|
||||||
"genesis": {
|
|
||||||
"runtimeGenesis": {
|
|
||||||
"code": "0x123",
|
|
||||||
"patch": {
|
|
||||||
"bar": {
|
|
||||||
"initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL"
|
|
||||||
},
|
|
||||||
"foo": {
|
|
||||||
"someEnum": {
|
|
||||||
"Data2": {
|
|
||||||
"values": "0x0c10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"someInteger": 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}),
|
|
||||||
"Output did not match expected"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[docify::export_content]
|
|
||||||
fn preset_4_json() {
|
|
||||||
assert_eq!(
|
|
||||||
pez_chain_spec_guide_runtime::presets::preset_4(),
|
|
||||||
json!({
|
|
||||||
"foo": {
|
|
||||||
"someEnum": {
|
|
||||||
"Data2": {
|
|
||||||
"values": "0x0c10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
//! # Bizinikiwi CLI
|
|
||||||
//!
|
|
||||||
//! Let's see some examples of typical CLI arguments used when setting up and running a
|
|
||||||
//! Bizinikiwi-based blockchain. We use the [`solochain-template`](https://github.com/pezkuwichain/pezkuwi-sdk/issues/195)
|
|
||||||
//! on these examples.
|
|
||||||
//!
|
|
||||||
//! #### Checking the available CLI arguments
|
|
||||||
//! ```bash
|
|
||||||
//! ./target/debug/node-template --help
|
|
||||||
//! ```
|
|
||||||
//! - `--help`: Displays the available CLI arguments.
|
|
||||||
//!
|
|
||||||
//! #### Starting a Local Bizinikiwi Node in Development Mode
|
|
||||||
//! ```bash
|
|
||||||
//! ./target/release/node-template \
|
|
||||||
//! --dev
|
|
||||||
//! ```
|
|
||||||
//! - `--dev`: Runs the node in development mode, using a pre-defined development chain
|
|
||||||
//! specification.
|
|
||||||
//! This mode ensures a fresh state by deleting existing data on restart.
|
|
||||||
//!
|
|
||||||
//! #### Generating Custom Chain Specification
|
|
||||||
//! ```bash
|
|
||||||
//! ./target/debug/node-template \
|
|
||||||
//! build-spec \
|
|
||||||
//! --disable-default-bootnode \
|
|
||||||
//! --chain local \
|
|
||||||
//! > customSpec.json
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! - `build-spec`: A subcommand to generate a chain specification file.
|
|
||||||
//! - `--disable-default-bootnode`: Disables the default bootnodes in the node template.
|
|
||||||
//! - `--chain local`: Indicates the chain specification is for a local development chain.
|
|
||||||
//! - `> customSpec.json`: Redirects the output into a customSpec.json file.
|
|
||||||
//!
|
|
||||||
//! #### Converting Chain Specification to Raw Format
|
|
||||||
//! ```bash
|
|
||||||
//! ./target/debug/node-template build-spec \
|
|
||||||
//! --chain=customSpec.json \
|
|
||||||
//! --raw \
|
|
||||||
//! --disable-default-bootnode \
|
|
||||||
//! > customSpecRaw.json
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! - `--chain=customSpec.json`: Uses the custom chain specification as input.
|
|
||||||
//! - `--disable-default-bootnode`: Disables the default bootnodes in the node template.
|
|
||||||
//! - `--raw`: Converts the chain specification into a raw format with encoded storage keys.
|
|
||||||
//! - `> customSpecRaw.json`: Outputs to `customSpecRaw.json`.
|
|
||||||
//!
|
|
||||||
//! #### Starting the First Node in a Private Network
|
|
||||||
//! ```bash
|
|
||||||
//! ./target/debug/node-template \
|
|
||||||
//! --base-path /tmp/node01 \
|
|
||||||
//! --chain ./customSpecRaw.json \
|
|
||||||
//! --port 30333 \
|
|
||||||
//! --ws-port 9945 \
|
|
||||||
//! --rpc-port 9933 \
|
|
||||||
//! --telemetry-url "wss://telemetry.pezkuwichain.io/submit/ 0" \
|
|
||||||
//! --validator \
|
|
||||||
//! --rpc-methods Unsafe \
|
|
||||||
//! --name MyNode01
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! - `--base-path`: Sets the directory for node data.
|
|
||||||
//! - `--chain`: Specifies the chain specification file.
|
|
||||||
//! - `--port`: TCP port for peer-to-peer communication.
|
|
||||||
//! - `--ws-port`: WebSocket port for RPC.
|
|
||||||
//! - `--rpc-port`: HTTP port for JSON-RPC.
|
|
||||||
//! - `--telemetry-url`: Endpoint for sending telemetry data.
|
|
||||||
//! - `--validator`: Indicates the node’s participation in block production.
|
|
||||||
//! - `--rpc-methods Unsafe`: Allows potentially unsafe RPC methods.
|
|
||||||
//! - `--name`: Sets a human-readable name for the node.
|
|
||||||
//!
|
|
||||||
//! #### Adding a Second Node to the Network
|
|
||||||
//! ```bash
|
|
||||||
//! ./target/release/node-template \
|
|
||||||
//! --base-path /tmp/bob \
|
|
||||||
//! --chain local \
|
|
||||||
//! --bob \
|
|
||||||
//! --port 30334 \
|
|
||||||
//! --rpc-port 9946 \
|
|
||||||
//! --telemetry-url "wss://telemetry.pezkuwichain.io/submit/ 0" \
|
|
||||||
//! --validator \
|
|
||||||
//! --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! - `--base-path`: Sets the directory for node data.
|
|
||||||
//! - `--chain`: Specifies the chain specification file.
|
|
||||||
//! - `--bob`: Initializes the node with the session keys of the "Bob" account.
|
|
||||||
//! - `--port`: TCP port for peer-to-peer communication.
|
|
||||||
//! - `--rpc-port`: HTTP port for JSON-RPC.
|
|
||||||
//! - `--telemetry-url`: Endpoint for sending telemetry data.
|
|
||||||
//! - `--validator`: Indicates the node’s participation in block production.
|
|
||||||
//! - `--bootnodes`: Specifies the address of the first node for peer discovery. Nodes should find
|
|
||||||
//! each other using mDNS. This command needs to be used if they don't find each other.
|
|
||||||
//!
|
|
||||||
//! ---
|
|
||||||
//!
|
|
||||||
//! > If you are interested in learning how to extend the CLI with your custom arguments, you can
|
|
||||||
//! > check out the [Customize your Bizinikiwi chain CLI](https://www.youtube.com/watch?v=IVifko1fqjw)
|
|
||||||
//! > seminar.
|
|
||||||
//! > Please note that the seminar is based on an older version of Bizinikiwi, and [Clap](https://docs.rs/clap/latest/clap/)
|
|
||||||
//! > is now used instead of [StructOpt](https://docs.rs/structopt/latest/structopt/) for parsing
|
|
||||||
//! > CLI arguments.
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
//! # Custom Host Functions
|
|
||||||
//!
|
|
||||||
//! Host functions are functions that the wasm instance can use to communicate with the node. Learn
|
|
||||||
//! more about this in [`crate::reference_docs::wasm_meta_protocol`].
|
|
||||||
//!
|
|
||||||
//! ## Finding Host Functions
|
|
||||||
//!
|
|
||||||
//! To declare a set of functions as host functions, you need to use the `#[runtime_interface]`
|
|
||||||
//! ([`pezsp_runtime_interface`]) attribute macro. The most notable set of host functions are those
|
|
||||||
//! that allow the runtime to access the chain state, namely [`pezsp_io::storage`]. Some other
|
|
||||||
//! notable host functions are also defined in [`pezsp_io`].
|
|
||||||
//!
|
|
||||||
//! ## Adding New Host Functions
|
|
||||||
//!
|
|
||||||
//! > Adding a new host function is a big commitment and should be done with care. Namely, the nodes
|
|
||||||
//! > in the network need to support all host functions forever in order to be able to sync
|
|
||||||
//! > historical blocks.
|
|
||||||
//!
|
|
||||||
//! Adding host functions is only possible when you are using a node-template, so that you have
|
|
||||||
//! access to the boilerplate of building your node.
|
|
||||||
//!
|
|
||||||
//! A group of host functions can always be grouped to gether as a tuple:
|
|
||||||
#![doc = docify::embed!("../../bizinikiwi/primitives/io/src/lib.rs", BizinikiwiHostFunctions)]
|
|
||||||
//!
|
|
||||||
//! The host functions are attached to the node side's [`pezsc_executor::WasmExecutor`]. For example
|
|
||||||
//! in the minimal template, the setup looks as follows:
|
|
||||||
#![doc = docify::embed!("../../templates/minimal/node/src/service.rs", FullClient)]
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
//! # Custom RPC do's and don'ts
|
|
||||||
//!
|
|
||||||
//! **TLDR:** Don't create new custom RPCs. Instead, rely on custom Runtime APIs, combined with
|
|
||||||
//! `state_call`.
|
|
||||||
//!
|
|
||||||
//! ## Background
|
|
||||||
//!
|
|
||||||
//! Pezkuwi-SDK offers the ability to query and subscribe storages directly. However what it does
|
|
||||||
//! not have is [view functions](https://github.com/pezkuwichain/pezkuwi-sdk/issues/247). This is an
|
|
||||||
//! essential feature to avoid duplicated logic between runtime and the client SDK. Custom RPC was
|
|
||||||
//! used as a solution. It allow the RPC node to expose new RPCs that clients can be used to query
|
|
||||||
//! computed properties.
|
|
||||||
//!
|
|
||||||
//! ## Problems with Custom RPC
|
|
||||||
//!
|
|
||||||
//! Unfortunately, custom RPC comes with many problems. To list a few:
|
|
||||||
//!
|
|
||||||
//! - It is offchain logic executed by the RPC node and therefore the client has to trust the RPC
|
|
||||||
//! node.
|
|
||||||
//! - To upgrade or add a new RPC logic, the RPC node has to be upgraded. This can cause significant
|
|
||||||
//! trouble when the RPC infrastructure is decentralized as we will need to coordinate multiple
|
|
||||||
//! parties to upgrade the RPC nodes.
|
|
||||||
//! - A lot of boilerplate code is required to add custom RPC.
|
|
||||||
//! - It prevents dApps from using a light client or an alternative client.
|
|
||||||
//! - It makes ecosystem tooling integration much more complicated. For example, dApps will not
|
|
||||||
//! be able to use [Chopsticks](https://github.com/AcalaNetwork/chopsticks) for testing as
|
|
||||||
//! Chopsticks will not have the custom RPC implementation.
|
|
||||||
//! - Poorly implemented custom RPC can be a DoS vector.
|
|
||||||
//!
|
|
||||||
//! Hence, we should avoid custom RPC.
|
|
||||||
//!
|
|
||||||
//! ## Alternatives
|
|
||||||
//!
|
|
||||||
//! Generally, [`pezsc_rpc::state::StateBackend::call`] aka. `state_call` should be used instead of
|
|
||||||
//! custom RPC.
|
|
||||||
//!
|
|
||||||
//! Usually, each custom RPC comes with a corresponding runtime API which implements the business
|
|
||||||
//! logic. So instead of invoke the custom RPC, we can use `state_call` to invoke the runtime API
|
|
||||||
//! directly. This is a trivial change on the dApp and no change on the runtime side. We may remove
|
|
||||||
//! the custom RPC from the node side if wanted.
|
|
||||||
//!
|
|
||||||
//! There are some other cases that a simple runtime API is not enough. For example, implementation
|
|
||||||
//! of Ethereum RPC requires an additional offchain database to index transactions. In this
|
|
||||||
//! particular case, we can have the RPC implemented on another client.
|
|
||||||
//!
|
|
||||||
//! For example, the Acala EVM+ RPC are implemented by
|
|
||||||
//! [eth-rpc-adapter](https://github.com/AcalaNetwork/bodhi.js/tree/master/packages/eth-rpc-adapter).
|
|
||||||
//! Alternatively, the [Frontier](https://github.com/polkadot-evm/frontier) project also provided
|
|
||||||
//! Ethereum RPC compatibility directly in the node-side software.
|
|
||||||
//!
|
|
||||||
//! ## Create a new Runtime API
|
|
||||||
//!
|
|
||||||
//! For example, let's take a look at the process through which the account nonce can be queried
|
|
||||||
//! through an RPC. First, a new runtime-api needs to be declared:
|
|
||||||
#![doc = docify::embed!("../../bizinikiwi/pezframe/system/rpc/runtime-api/src/lib.rs", AccountNonceApi)]
|
|
||||||
//!
|
|
||||||
//! This API is implemented at the runtime level, always inside [`pezsp_api::impl_runtime_apis!`].
|
|
||||||
//!
|
|
||||||
//! As noted, this is already enough to make this API usable via `state_call`.
|
|
||||||
//!
|
|
||||||
//! ## Create a new custom RPC (Legacy)
|
|
||||||
//!
|
|
||||||
//! Should you wish to implement the legacy approach of exposing this runtime-api as a custom
|
|
||||||
//! RPC-api, then a custom RPC server has to be defined.
|
|
||||||
#![doc = docify::embed!("../../bizinikiwi/utils/pezframe/rpc/system/src/lib.rs", SystemApi)]
|
|
||||||
//!
|
|
||||||
//! ## Add a new RPC to the node (Legacy)
|
|
||||||
//!
|
|
||||||
//! Finally, this custom RPC needs to be integrated into the node side. This is usually done in a
|
|
||||||
//! `rpc.rs` in a typical template, as follows:
|
|
||||||
#![doc = docify::embed!("../../templates/minimal/node/src/rpc.rs", create_full)]
|
|
||||||
//!
|
|
||||||
//! ## Future
|
|
||||||
//!
|
|
||||||
//! - [XCQ](https://forum.polkadot.network/t/cross-consensus-query-language-xcq/7583) will be a good
|
|
||||||
//! solution for most of the query needs.
|
|
||||||
//! - [New JSON-RPC Specification](https://github.com/paritytech/json-rpc-interface-spec)
|
|
||||||
@@ -1,396 +0,0 @@
|
|||||||
// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
//! [Defensive programming](https://en.wikipedia.org/wiki/Defensive_programming) is a design paradigm that enables a program to continue
|
|
||||||
//! running despite unexpected behavior, input, or events that may arise in runtime.
|
|
||||||
//! Usually, unforeseen circumstances may cause the program to stop or, in the Rust context,
|
|
||||||
//! `panic!`. Defensive practices allow for these circumstances to be accounted for ahead of time
|
|
||||||
//! and for them to be handled gracefully, which is in line with the intended fault-tolerant and
|
|
||||||
//! deterministic nature of blockchains.
|
|
||||||
//!
|
|
||||||
//! The Pezkuwi SDK is built to reflect these principles and to facilitate their usage accordingly.
|
|
||||||
//!
|
|
||||||
//! ## General Overview
|
|
||||||
//!
|
|
||||||
//! When developing within the context of the Bizinikiwi runtime, there is one golden rule:
|
|
||||||
//!
|
|
||||||
//! ***DO NOT PANIC***. There are some exceptions, but generally, this is the default precedent.
|
|
||||||
//!
|
|
||||||
//! > It’s important to differentiate between the runtime and node. The runtime refers to the core
|
|
||||||
//! > business logic of a Bizinikiwi-based chain, whereas the node refers to the outer client, which
|
|
||||||
//! > deals with telemetry and gossip from other nodes. For more information, read about
|
|
||||||
//! > [Bizinikiwi's node
|
|
||||||
//! > architecture](crate::reference_docs::wasm_meta_protocol#node-vs-runtime). It’s also important
|
|
||||||
//! > to note that the criticality of the node is slightly lesser
|
|
||||||
//! > than that of the runtime, which is why you may see `unwrap()` or other “non-defensive”
|
|
||||||
//! > approaches
|
|
||||||
//! in a few places of the node's code repository.
|
|
||||||
//!
|
|
||||||
//! Most of these practices fall within Rust's
|
|
||||||
//! colloquial usage of proper error propagation, handling, and arithmetic-based edge cases.
|
|
||||||
//!
|
|
||||||
//! General guidelines:
|
|
||||||
//!
|
|
||||||
//! - **Avoid writing functions that could explicitly panic,** such as directly using `unwrap()` on
|
|
||||||
//! a [`Result`], or accessing an out-of-bounds index on a collection. Safer methods to access
|
|
||||||
//! collection types, i.e., `get()` which allow defensive handling of the resulting [`Option`] are
|
|
||||||
//! recommended to be used.
|
|
||||||
//! - **It may be acceptable to use `except()`,** but only if one is completely certain (and has
|
|
||||||
//! performed a check beforehand) that a value won't panic upon unwrapping. *Even this is
|
|
||||||
//! discouraged*, however, as future changes to that function could then cause that statement to
|
|
||||||
//! panic. It is important to ensure all possible errors are propagated and handled effectively.
|
|
||||||
//! - **If a function *can* panic,** it usually is prefaced with `unchecked_` to indicate its
|
|
||||||
//! unsafety.
|
|
||||||
//! - **If you are writing a function that could panic,** [document it!](https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html#documenting-components)
|
|
||||||
//! - **Carefully handle mathematical operations.** Many seemingly, simplistic operations, such as
|
|
||||||
//! **arithmetic** in the runtime, could present a number of issues [(see more later in this
|
|
||||||
//! document)](#integer-overflow). Use checked arithmetic wherever possible.
|
|
||||||
//!
|
|
||||||
//! These guidelines could be summarized in the following example, where `bad_pop` is prone to
|
|
||||||
//! panicking, and `good_pop` allows for proper error handling to take place:
|
|
||||||
//!
|
|
||||||
//!```ignore
|
|
||||||
//! // Bad pop always requires that we return something, even if vector/array is empty.
|
|
||||||
//! fn bad_pop<T>(v: Vec<T>) -> T {}
|
|
||||||
//! // Good pop allows us to return None from the Option if need be.
|
|
||||||
//! fn good_pop<T>(v: Vec<T>) -> Option<T> {}
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! ### Defensive Traits
|
|
||||||
//!
|
|
||||||
//! The [`Defensive`](pezframe::traits::Defensive) trait provides a number of functions, all of which
|
|
||||||
//! provide an alternative to 'vanilla' Rust functions, e.g.:
|
|
||||||
//!
|
|
||||||
//! - [`defensive_unwrap_or()`](pezframe::traits::Defensive::defensive_unwrap_or) instead of
|
|
||||||
//! `unwrap_or()`
|
|
||||||
//! - [`defensive_ok_or()`](pezframe::traits::DefensiveOption::defensive_ok_or) instead of `ok_or()`
|
|
||||||
//!
|
|
||||||
//! Defensive methods use [`debug_assertions`](https://doc.rust-lang.org/reference/conditional-compilation.html#debug_assertions), which panic in development, but in
|
|
||||||
//! production/release, they will merely log an error (i.e., `log::error`).
|
|
||||||
//!
|
|
||||||
//! The [`Defensive`](pezframe::traits::Defensive) trait and its various implementations can be found
|
|
||||||
//! [here](pezframe::traits::Defensive).
|
|
||||||
//!
|
|
||||||
//! ## Integer Overflow
|
|
||||||
//!
|
|
||||||
//! The Rust compiler prevents static overflow from happening at compile time.
|
|
||||||
//! The compiler panics in **debug** mode in the event of an integer overflow. In
|
|
||||||
//! **release** mode, it resorts to silently _wrapping_ the overflowed amount in a modular fashion
|
|
||||||
//! (from the `MAX` back to zero).
|
|
||||||
//!
|
|
||||||
//! In runtime development, we don't always have control over what is being supplied
|
|
||||||
//! as a parameter. For example, even this simple add function could present one of two outcomes
|
|
||||||
//! depending on whether it is in **release** or **debug** mode:
|
|
||||||
//!
|
|
||||||
//! ```ignore
|
|
||||||
//! fn naive_add(x: u8, y: u8) -> u8 {
|
|
||||||
//! x + y
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//! If we passed overflow-able values at runtime, this could panic (or wrap if in release).
|
|
||||||
//!
|
|
||||||
//! ```ignore
|
|
||||||
//! naive_add(250u8, 10u8); // In debug mode, this would panic. In release, this would return 4.
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! It is the silent portion of this behavior that presents a real issue. Such behavior should be
|
|
||||||
//! made obvious, especially in blockchain development, where unsafe arithmetic could produce
|
|
||||||
//! unexpected consequences like a user balance over or underflowing.
|
|
||||||
//!
|
|
||||||
//! Fortunately, there are ways to both represent and handle these scenarios depending on our
|
|
||||||
//! specific use case natively built into Rust and libraries like [`pezsp_arithmetic`].
|
|
||||||
//!
|
|
||||||
//! ## Infallible Arithmetic
|
|
||||||
//!
|
|
||||||
//! Both Rust and Bizinikiwi provide safe ways to deal with numbers and alternatives to floating
|
|
||||||
//! point arithmetic.
|
|
||||||
//!
|
|
||||||
//! Known scenarios that could be fallible should be avoided: i.e., avoiding the possibility of
|
|
||||||
//! dividing/modulo by zero at any point should be mitigated. One should be opting for a
|
|
||||||
//! `checked_*` method to introduce safe arithmetic in their code in most cases.
|
|
||||||
//!
|
|
||||||
//! A developer should use fixed-point instead of floating-point arithmetic to mitigate the
|
|
||||||
//! potential for inaccuracy, rounding errors, or other unexpected behavior.
|
|
||||||
//!
|
|
||||||
//! - [Fixed point types](pezsp_arithmetic::fixed_point) and their associated usage can be found
|
|
||||||
//! here.
|
|
||||||
//! - [PerThing](pezsp_arithmetic::per_things) and its associated types can be found here.
|
|
||||||
//!
|
|
||||||
//! Using floating point number types (i.e. f32, f64) in the runtime should be avoided, as a single non-deterministic result could cause chaos for blockchain consensus along with the issues above. For more on the specifics of the peculiarities of floating point calculations, [watch this video by the Computerphile](https://www.youtube.com/watch?v=PZRI1IfStY0).
|
|
||||||
//!
|
|
||||||
//! The following methods demonstrate different ways to handle numbers natively in Rust safely,
|
|
||||||
//! without fear of panic or unexpected behavior from wrapping.
|
|
||||||
//!
|
|
||||||
//! ### Checked Arithmetic
|
|
||||||
//!
|
|
||||||
//! **Checked operations** utilize an `Option<T>` as a return type. This allows for
|
|
||||||
//! catching any unexpected behavior in the event of an overflow through simple pattern matching.
|
|
||||||
//!
|
|
||||||
//! This is an example of a valid operation:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/defensive_programming.rs", checked_add_example)]
|
|
||||||
//!
|
|
||||||
//! This is an example of an invalid operation. In this case, a simulated integer overflow, which
|
|
||||||
//! would simply result in `None`:
|
|
||||||
#![doc = docify::embed!(
|
|
||||||
"./src/reference_docs/defensive_programming.rs",
|
|
||||||
checked_add_handle_error_example
|
|
||||||
)]
|
|
||||||
//!
|
|
||||||
//! Suppose you aren’t sure which operation to use for runtime math. In that case, checked
|
|
||||||
//! operations are the safest bet, presenting two predictable (and erroring) outcomes that can be
|
|
||||||
//! handled accordingly (Some and None).
|
|
||||||
//!
|
|
||||||
//! The following conventions can be seen within the Pezkuwi SDK, where it is
|
|
||||||
//! handled in two ways:
|
|
||||||
//!
|
|
||||||
//! - As an [`Option`], using the `if let` / `if` or `match`
|
|
||||||
//! - As a [`Result`], via `ok_or` (or similar conversion to [`Result`] from [`Option`])
|
|
||||||
//!
|
|
||||||
//! #### Handling via Option - More Verbose
|
|
||||||
//!
|
|
||||||
//! Because wrapped operations return `Option<T>`, you can use a more verbose/explicit form of error
|
|
||||||
//! handling via `if` or `if let`:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/defensive_programming.rs", increase_balance)]
|
|
||||||
//!
|
|
||||||
//! Optionally, match may also be directly used in a more concise manner:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/defensive_programming.rs", increase_balance_match)]
|
|
||||||
//!
|
|
||||||
//! This is generally a useful convention for handling checked types and most types that return
|
|
||||||
//! `Option<T>`.
|
|
||||||
//!
|
|
||||||
//! #### Handling via Result - Less Verbose
|
|
||||||
//!
|
|
||||||
//! In the Pezkuwi SDK codebase, checked operations are handled as a `Result` via `ok_or`. This is
|
|
||||||
//! a less verbose way of expressing the above. This usage often boils down to the developer’s
|
|
||||||
//! preference:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/defensive_programming.rs", increase_balance_result)]
|
|
||||||
//!
|
|
||||||
//! ### Saturating Operations
|
|
||||||
//!
|
|
||||||
//! Saturating a number limits it to the type’s upper or lower bound, even if the integer type
|
|
||||||
//! overflowed in runtime. For example, adding to `u32::MAX` would simply limit itself to
|
|
||||||
//! `u32::MAX`:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/defensive_programming.rs", saturated_add_example)]
|
|
||||||
//!
|
|
||||||
//! Saturating calculations can be used if one is very sure that something won't overflow, but wants
|
|
||||||
//! to avoid introducing the notion of any potential-panic or wrapping behavior.
|
|
||||||
//!
|
|
||||||
//! There is also a series of defensive alternatives via
|
|
||||||
//! [`DefensiveSaturating`](pezframe::traits::DefensiveSaturating), which introduces the same behavior
|
|
||||||
//! of the [`Defensive`](pezframe::traits::Defensive) trait, only with saturating, mathematical
|
|
||||||
//! operations:
|
|
||||||
#![doc = docify::embed!(
|
|
||||||
"./src/reference_docs/defensive_programming.rs",
|
|
||||||
saturated_defensive_example
|
|
||||||
)]
|
|
||||||
//!
|
|
||||||
//! ### Mathematical Operations in Bizinikiwi Development - Further Context
|
|
||||||
//!
|
|
||||||
//! As a recap, we covered the following concepts:
|
|
||||||
//!
|
|
||||||
//! 1. **Checked** operations - using [`Option`] or [`Result`]
|
|
||||||
//! 2. **Saturating** operations - limited to the lower and upper bounds of a number type
|
|
||||||
//! 3. **Wrapped** operations (the default) - wrap around to above or below the bounds of a type
|
|
||||||
//!
|
|
||||||
//! #### The problem with 'default' wrapped operations
|
|
||||||
//!
|
|
||||||
//! **Wrapped operations** cause the overflow to wrap around to either the maximum or minimum of
|
|
||||||
//! that type. Imagine this in the context of a blockchain, where there are account balances, voting
|
|
||||||
//! counters, nonces for transactions, and other aspects of a blockchain.
|
|
||||||
//!
|
|
||||||
//! While it may seem trivial, choosing how to handle numbers is quite important. As a thought
|
|
||||||
//! exercise, here are some scenarios of which will shed more light on when to use which.
|
|
||||||
//!
|
|
||||||
//! #### Bob's Overflowed Balance
|
|
||||||
//!
|
|
||||||
//! **Bob's** balance exceeds the `Balance` type on the `EduChain`. Because the pezpallet developer
|
|
||||||
//! did not handle the calculation to add to Bob's balance with any regard to this overflow,
|
|
||||||
//! **Bob's** balance is now essentially `0`, the operation **wrapped**.
|
|
||||||
//!
|
|
||||||
//! <details>
|
|
||||||
//! <summary><b>Solution: Saturating or Checked</b></summary>
|
|
||||||
//! For Bob's balance problems, using a `saturating_add` or `checked_add` could've mitigated
|
|
||||||
//! this issue. They simply would've reached the upper, or lower bounds, of the particular type for
|
|
||||||
//! an on-chain balance. In other words: Bob's balance would've stayed at the maximum of the
|
|
||||||
//! Balance type. </details>
|
|
||||||
//!
|
|
||||||
//! #### Alice's 'Underflowed' Balance
|
|
||||||
//!
|
|
||||||
//! Alice’s balance has reached `0` after a transfer to Bob. Suddenly, she has been slashed on
|
|
||||||
//! EduChain, causing her balance to reach near the limit of `u32::MAX` - a very large amount - as
|
|
||||||
//! wrapped operations can go both ways. Alice can now successfully vote using her new, overpowered
|
|
||||||
//! token balance, destroying the chain's integrity.
|
|
||||||
//!
|
|
||||||
//! <details>
|
|
||||||
//! <summary><b>Solution: Saturating</b></summary>
|
|
||||||
//! For Alice's balance problem, using `saturated_sub` could've mitigated this issue. A saturating
|
|
||||||
//! calculation would've simply limited her balance to the lower bound of u32, as having a negative
|
|
||||||
//! balance is not a concept within blockchains. In other words: Alice's balance would've stayed
|
|
||||||
//! at "0", even after being slashed.
|
|
||||||
//!
|
|
||||||
//! This is also an example that while one system may work in isolation, shared interfaces, such
|
|
||||||
//! as the notion of balances, are often shared across multiple pallets - meaning these small
|
|
||||||
//! changes can make a big difference depending on the scenario. </details>
|
|
||||||
//!
|
|
||||||
//! #### Proposal ID Overwrite
|
|
||||||
//!
|
|
||||||
//! A `u8` parameter, called `proposals_count`, represents the type for counting the number of
|
|
||||||
//! proposals on-chain. Every time a new proposal is added to the system, this number increases.
|
|
||||||
//! With the proposal pezpallet's high usage, it has reached `u8::MAX`’s limit of 255, causing
|
|
||||||
//! `proposals_count` to go to 0. Unfortunately, this results in new proposals overwriting old ones,
|
|
||||||
//! effectively erasing any notion of past proposals!
|
|
||||||
//!
|
|
||||||
//! <details>
|
|
||||||
//! <summary><b>Solution: Checked</b></summary>
|
|
||||||
//! For the proposal IDs, proper handling via `checked` math would've been suitable,
|
|
||||||
//! Saturating could've been used - but it also would've 'failed' silently. Using `checked_add` to
|
|
||||||
//! ensure that the next proposal ID would've been valid would've been a viable way to let the user
|
|
||||||
//! know the state of their proposal:
|
|
||||||
//!
|
|
||||||
//! ```ignore
|
|
||||||
//! let next_proposal_id = current_count.checked_add(1).ok_or_else(|| Error::TooManyProposals)?;
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! </details>
|
|
||||||
//!
|
|
||||||
//! From the above, we can clearly see the problematic nature of seemingly simple operations in the
|
|
||||||
//! runtime, and care should be given to ensure a defensive approach is taken.
|
|
||||||
//!
|
|
||||||
//! ### Edge cases of `panic!`-able instances in Bizinikiwi
|
|
||||||
//!
|
|
||||||
//! As you traverse through the codebase (particularly in `bizinikiwi/frame`, where the majority of
|
|
||||||
//! runtime code lives), you may notice that there (only a few!) occurrences where `panic!` is used
|
|
||||||
//! explicitly. This is used when the runtime should stall, rather than keep running, as that is
|
|
||||||
//! considered safer. Particularly when it comes to mission-critical components, such as block
|
|
||||||
//! authoring, consensus, or other protocol-level dependencies, going through with an action may
|
|
||||||
//! actually cause harm to the network, and thus stalling would be the better option.
|
|
||||||
//!
|
|
||||||
//! Take the example of the BABE pezpallet ([`pezpallet_babe`]), which doesn't allow for a validator
|
|
||||||
//! to participate if it is disabled (see: [`pezframe::traits::DisabledValidators`]):
|
|
||||||
//!
|
|
||||||
//! ```ignore
|
|
||||||
//! if T::DisabledValidators::is_disabled(authority_index) {
|
|
||||||
//! panic!(
|
|
||||||
//! "Validator with index {:?} is disabled and should not be attempting to author blocks.",
|
|
||||||
//! authority_index,
|
|
||||||
//! );
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! There are other examples in various pallets, mostly those crucial to the blockchain’s
|
|
||||||
//! functionality. Most of the time, you will not be writing pallets which operate at this level,
|
|
||||||
//! but these exceptions should be noted regardless.
|
|
||||||
//!
|
|
||||||
//! ## Other Resources
|
|
||||||
//!
|
|
||||||
//! - [PBA Lectures on YouTube](https://www.youtube.com/playlist?list=PL-w_i5kwVqbni1Ch2j_RwTIXiB-bwnYqq)
|
|
||||||
#![allow(dead_code)]
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
mod fake_runtime_types {
|
|
||||||
// Note: The following types are purely for the purpose of example, and do not contain any
|
|
||||||
// *real* use case other than demonstrating various concepts.
|
|
||||||
pub enum RuntimeError {
|
|
||||||
Overflow,
|
|
||||||
UserDoesntExist,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Address = ();
|
|
||||||
|
|
||||||
pub struct Runtime;
|
|
||||||
|
|
||||||
impl Runtime {
|
|
||||||
fn get_balance(account: Address) -> Result<u64, RuntimeError> {
|
|
||||||
Ok(0u64)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_balance(account: Address, new_balance: u64) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
fn increase_balance(account: Address, amount: u64) -> Result<(), RuntimeError> {
|
|
||||||
// Get a user's current balance
|
|
||||||
let balance = Runtime::get_balance(account)?;
|
|
||||||
// SAFELY increase the balance by some amount
|
|
||||||
if let Some(new_balance) = balance.checked_add(amount) {
|
|
||||||
Runtime::set_balance(account, new_balance);
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(RuntimeError::Overflow)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
fn increase_balance_match(account: Address, amount: u64) -> Result<(), RuntimeError> {
|
|
||||||
// Get a user's current balance
|
|
||||||
let balance = Runtime::get_balance(account)?;
|
|
||||||
// SAFELY increase the balance by some amount
|
|
||||||
let new_balance = match balance.checked_add(amount) {
|
|
||||||
Some(balance) => balance,
|
|
||||||
None => {
|
|
||||||
return Err(RuntimeError::Overflow);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Runtime::set_balance(account, new_balance);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
fn increase_balance_result(account: Address, amount: u64) -> Result<(), RuntimeError> {
|
|
||||||
// Get a user's current balance
|
|
||||||
let balance = Runtime::get_balance(account)?;
|
|
||||||
// SAFELY increase the balance by some amount - this time, by using `ok_or`
|
|
||||||
let new_balance = balance.checked_add(amount).ok_or(RuntimeError::Overflow)?;
|
|
||||||
Runtime::set_balance(account, new_balance);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use pezframe::traits::DefensiveSaturating;
|
|
||||||
#[docify::export]
|
|
||||||
#[test]
|
|
||||||
fn checked_add_example() {
|
|
||||||
// This is valid, as 20 is perfectly within the bounds of u32.
|
|
||||||
let add = (10u32).checked_add(10);
|
|
||||||
assert_eq!(add, Some(20))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[test]
|
|
||||||
fn checked_add_handle_error_example() {
|
|
||||||
// This is invalid - we are adding something to the max of u32::MAX, which would overflow.
|
|
||||||
// Luckily, checked_add just marks this as None!
|
|
||||||
let add = u32::MAX.checked_add(10);
|
|
||||||
assert_eq!(add, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[test]
|
|
||||||
fn saturated_add_example() {
|
|
||||||
// Saturating add simply saturates
|
|
||||||
// to the numeric bound of that type if it overflows.
|
|
||||||
let add = u32::MAX.saturating_add(10);
|
|
||||||
assert_eq!(add, u32::MAX)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[test]
|
|
||||||
#[cfg_attr(debug_assertions, should_panic(expected = "Defensive failure has been triggered!"))]
|
|
||||||
fn saturated_defensive_example() {
|
|
||||||
let saturated_defensive = u32::MAX.defensive_saturating_add(10);
|
|
||||||
assert_eq!(saturated_defensive, u32::MAX);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,223 +0,0 @@
|
|||||||
//! # Development Environment Advice
|
|
||||||
//!
|
|
||||||
//! Large Rust projects are known for sometimes long compile times and sluggish dev tooling, and
|
|
||||||
//! pezkuwi-sdk is no exception.
|
|
||||||
//!
|
|
||||||
//! This page contains some advice to improve your workflow when using common tooling.
|
|
||||||
//!
|
|
||||||
//! ## Rust Analyzer Configuration
|
|
||||||
//!
|
|
||||||
//! [Rust Analyzer](https://rust-analyzer.github.io/) is the defacto [LSP](https://langserver.org/) for Rust. Its default
|
|
||||||
//! settings are fine for smaller projects, but not well optimised for pezkuwi-sdk.
|
|
||||||
//!
|
|
||||||
//! Below is a suggested configuration for VSCode or any VSCode-based editor like Cursor:
|
|
||||||
//!
|
|
||||||
//! ```json
|
|
||||||
//! {
|
|
||||||
//! // Use a separate target dir for Rust Analyzer. Helpful if you want to use Rust
|
|
||||||
//! // Analyzer and cargo on the command line at the same time,
|
|
||||||
//! // at the expense of duplicating build artifacts.
|
|
||||||
//! "rust-analyzer.cargo.targetDir": "target/vscode-rust-analyzer",
|
|
||||||
//! // Improve stability
|
|
||||||
//! "rust-analyzer.server.extraEnv": {
|
|
||||||
//! "CHALK_OVERFLOW_DEPTH": "100000000",
|
|
||||||
//! "CHALK_SOLVER_MAX_SIZE": "10000000"
|
|
||||||
//! },
|
|
||||||
//! // Check feature-gated code
|
|
||||||
//! "rust-analyzer.cargo.features": "all",
|
|
||||||
//! "rust-analyzer.cargo.extraEnv": {
|
|
||||||
//! // Skip building WASM, there is never need for it here
|
|
||||||
//! "SKIP_WASM_BUILD": "1"
|
|
||||||
//! },
|
|
||||||
//! // Don't expand some problematic proc_macros
|
|
||||||
//! "rust-analyzer.procMacro.ignored": {
|
|
||||||
//! "async-trait": ["async_trait"],
|
|
||||||
//! "napi-derive": ["napi"],
|
|
||||||
//! "async-recursion": ["async_recursion"],
|
|
||||||
//! "async-std": ["async_std"]
|
|
||||||
//! },
|
|
||||||
//! // Use nightly formatting.
|
|
||||||
//! // See the pezkuwi-sdk CI job that checks formatting for the current version used in
|
|
||||||
//! // pezkuwi-sdk.
|
|
||||||
//! "rust-analyzer.rustfmt.extraArgs": ["+nightly-2024-04-10"],
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! and the same in Lua for `neovim/nvim-lspconfig`:
|
|
||||||
//!
|
|
||||||
//! ```lua
|
|
||||||
//! ["rust-analyzer"] = {
|
|
||||||
//! rust = {
|
|
||||||
//! # Use a separate target dir for Rust Analyzer. Helpful if you want to use Rust
|
|
||||||
//! # Analyzer and cargo on the command line at the same time.
|
|
||||||
//! analyzerTargetDir = "target/nvim-rust-analyzer",
|
|
||||||
//! },
|
|
||||||
//! server = {
|
|
||||||
//! # Improve stability
|
|
||||||
//! extraEnv = {
|
|
||||||
//! ["CHALK_OVERFLOW_DEPTH"] = "100000000",
|
|
||||||
//! ["CHALK_SOLVER_MAX_SIZE"] = "100000000",
|
|
||||||
//! },
|
|
||||||
//! },
|
|
||||||
//! cargo = {
|
|
||||||
//! # Check feature-gated code
|
|
||||||
//! features = "all",
|
|
||||||
//! extraEnv = {
|
|
||||||
//! # Skip building WASM, there is never need for it here
|
|
||||||
//! ["SKIP_WASM_BUILD"] = "1",
|
|
||||||
//! },
|
|
||||||
//! },
|
|
||||||
//! procMacro = {
|
|
||||||
//! # Don't expand some problematic proc_macros
|
|
||||||
//! ignored = {
|
|
||||||
//! ["async-trait"] = { "async_trait" },
|
|
||||||
//! ["napi-derive"] = { "napi" },
|
|
||||||
//! ["async-recursion"] = { "async_recursion" },
|
|
||||||
//! ["async-std"] = { "async_std" },
|
|
||||||
//! },
|
|
||||||
//! },
|
|
||||||
//! rustfmt = {
|
|
||||||
//! # Use nightly formatting.
|
|
||||||
//! # See the pezkuwi-sdk CI job that checks formatting for the current version used in
|
|
||||||
//! # pezkuwi-sdk.
|
|
||||||
//! extraArgs = { "+nightly-2024-04-10" },
|
|
||||||
//! },
|
|
||||||
//! },
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! Alternatively for neovim, if you are using [Rustaceanvim](https://github.com/mrcjkb/rustaceanvim),
|
|
||||||
//! you can achieve the same configuring `rust-analyzer` via `rustaceanvim` as follows:
|
|
||||||
//! ```lua
|
|
||||||
//! return {
|
|
||||||
//! {
|
|
||||||
//! "mrcjkb/rustaceanvim",
|
|
||||||
//! opts = {
|
|
||||||
//! server = {
|
|
||||||
//! default_settings = {
|
|
||||||
//! ["rust-analyzer"] = {
|
|
||||||
//! // put the same config as for nvim-lspconfig here
|
|
||||||
//! },
|
|
||||||
//! },
|
|
||||||
//! },
|
|
||||||
//! },
|
|
||||||
//! },
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! Similarly for Zed, you can replicate the same VSCode configuration in
|
|
||||||
//! `~/.config/zed/settings.json` as follows:
|
|
||||||
//! ```json
|
|
||||||
//! "lsp": {
|
|
||||||
//! "rust-analyzer": {
|
|
||||||
//! "initialization_options": {
|
|
||||||
//! // same config as for VSCode for rust, cargo, procMacros, ...
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! },
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! In general, refer to your favorite editor / IDE's documentation to properly configure
|
|
||||||
//! `rust-analyzer` as language server.
|
|
||||||
//! For the full set of configuration options see <https://rust-analyzer.github.io/manual.html#configuration>.
|
|
||||||
//!
|
|
||||||
//! ## Cargo Usage
|
|
||||||
//!
|
|
||||||
//! ### Using `--package` (a.k.a. `-p`)
|
|
||||||
//!
|
|
||||||
//! pezkuwi-sdk is a monorepo containing many crates. When you run a cargo command without
|
|
||||||
//! `-p`, you will almost certainly compile crates outside of the scope you are working.
|
|
||||||
//!
|
|
||||||
//! Instead, you should identify the name of the crate you are working on by checking the `name`
|
|
||||||
//! field in the closest `Cargo.toml` file. Then, use `-p` with your cargo commands to only compile
|
|
||||||
//! that crate.
|
|
||||||
//!
|
|
||||||
//! ### `SKIP_WASM_BUILD=1` environment variable
|
|
||||||
//!
|
|
||||||
//! When cargo touches a runtime crate, by default it will also compile the WASM binary,
|
|
||||||
//! approximately doubling the compilation time.
|
|
||||||
//!
|
|
||||||
//! The WASM binary is usually not needed, especially when running `check` or `test`. To skip the
|
|
||||||
//! WASM build, set the `SKIP_WASM_BUILD` environment variable to `1`. For example:
|
|
||||||
//! `SKIP_WASM_BUILD=1 cargo check -p pezframe-support`.
|
|
||||||
//!
|
|
||||||
//! ### Cargo Remote
|
|
||||||
//!
|
|
||||||
//! Warning: cargo remote by default doesn't transfer hidden files to the remote machine. But hidden
|
|
||||||
//! files can be useful, e.g. for sqlx usage. On the other hand using `--transfer-hidden` flag will
|
|
||||||
//! transfer `.git` which is big.
|
|
||||||
//!
|
|
||||||
//! If you have a powerful remote server available, you may consider using
|
|
||||||
//! [cargo-remote](https://github.com/sgeisler/cargo-remote) to execute cargo commands on it,
|
|
||||||
//! freeing up local resources for other tasks like `rust-analyzer`.
|
|
||||||
//!
|
|
||||||
//! When using `cargo-remote`, you can configure your editor to perform the the typical
|
|
||||||
//! "check-on-save" remotely as well. The configuration for VSCode (or any VSCode-based editor like
|
|
||||||
//! Cursor) is as follows:
|
|
||||||
//!
|
|
||||||
//! ```json
|
|
||||||
//! {
|
|
||||||
//! "rust-analyzer.cargo.buildScripts.overrideCommand": [
|
|
||||||
//! "cargo",
|
|
||||||
//! "remote",
|
|
||||||
//! "--build-env",
|
|
||||||
//! "SKIP_WASM_BUILD=1",
|
|
||||||
//! "--",
|
|
||||||
//! "check",
|
|
||||||
//! "--message-format=json",
|
|
||||||
//! "--all-targets",
|
|
||||||
//! "--all-features",
|
|
||||||
//! "--target-dir=target/rust-analyzer"
|
|
||||||
//! ],
|
|
||||||
//! "rust-analyzer.check.overrideCommand": [
|
|
||||||
//! "cargo",
|
|
||||||
//! "remote",
|
|
||||||
//! "--build-env",
|
|
||||||
//! "SKIP_WASM_BUILD=1",
|
|
||||||
//! "--",
|
|
||||||
//! "check",
|
|
||||||
//! "--workspace",
|
|
||||||
//! "--message-format=json",
|
|
||||||
//! "--all-targets",
|
|
||||||
//! "--all-features",
|
|
||||||
//! "--target-dir=target/rust-analyzer"
|
|
||||||
//! ],
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! and the same in Lua for `neovim/nvim-lspconfig`:
|
|
||||||
//!
|
|
||||||
//! ```lua
|
|
||||||
//! ["rust-analyzer"] = {
|
|
||||||
//! cargo = {
|
|
||||||
//! buildScripts = {
|
|
||||||
//! overrideCommand = {
|
|
||||||
//! "cargo",
|
|
||||||
//! "remote",
|
|
||||||
//! "--build-env",
|
|
||||||
//! "SKIP_WASM_BUILD=1",
|
|
||||||
//! "--",
|
|
||||||
//! "check",
|
|
||||||
//! "--message-format=json",
|
|
||||||
//! "--all-targets",
|
|
||||||
//! "--all-features",
|
|
||||||
//! "--target-dir=target/rust-analyzer"
|
|
||||||
//! },
|
|
||||||
//! },
|
|
||||||
//! },
|
|
||||||
//! check = {
|
|
||||||
//! overrideCommand = {
|
|
||||||
//! "cargo",
|
|
||||||
//! "remote",
|
|
||||||
//! "--build-env",
|
|
||||||
//! "SKIP_WASM_BUILD=1",
|
|
||||||
//! "--",
|
|
||||||
//! "check",
|
|
||||||
//! "--workspace",
|
|
||||||
//! "--message-format=json",
|
|
||||||
//! "--all-targets",
|
|
||||||
//! "--all-features",
|
|
||||||
//! "--target-dir=target/rust-analyzer"
|
|
||||||
//! },
|
|
||||||
//! },
|
|
||||||
//! },
|
|
||||||
//! ```
|
|
||||||
@@ -1,334 +0,0 @@
|
|||||||
//! # Constructing and Signing Extrinsics
|
|
||||||
//!
|
|
||||||
//! Extrinsics are payloads that are stored in blocks which are responsible for altering the state
|
|
||||||
//! of a blockchain via the [_state transition
|
|
||||||
//! function_][crate::reference_docs::blockchain_state_machines].
|
|
||||||
//!
|
|
||||||
//! Bizinikiwi is configurable enough that extrinsics can take any format. In practice, runtimes
|
|
||||||
//! tend to use our [`pezsp_runtime::generic::UncheckedExtrinsic`] type to represent extrinsics,
|
|
||||||
//! because it's generic enough to cater for most (if not all) use cases. In Pezkuwi, this is
|
|
||||||
//! configured [here](https://github.com/polkadot-fellows/runtimes/blob/94b2798b69ba6779764e20a50f056e48db78ebef/relay/polkadot/src/lib.rs#L1478)
|
|
||||||
//! at the time of writing.
|
|
||||||
//!
|
|
||||||
//! What follows is a description of how extrinsics based on this
|
|
||||||
//! [`pezsp_runtime::generic::UncheckedExtrinsic`] type are encoded into bytes. Specifically, we are
|
|
||||||
//! looking at how extrinsics with a format version of 5 are encoded. This version is itself a part
|
|
||||||
//! of the payload, and if it changes, it indicates that something about the encoding may have
|
|
||||||
//! changed.
|
|
||||||
//!
|
|
||||||
//! # Encoding an Extrinsic
|
|
||||||
//!
|
|
||||||
//! At a high level, all extrinsics compatible with [`pezsp_runtime::generic::UncheckedExtrinsic`]
|
|
||||||
//! are formed from concatenating some details together, as in the following pseudo-code:
|
|
||||||
//!
|
|
||||||
//! ```text
|
|
||||||
//! extrinsic_bytes = concat(
|
|
||||||
//! compact_encoded_length,
|
|
||||||
//! version_and_extrinsic_type,
|
|
||||||
//! maybe_extension_data,
|
|
||||||
//! call_data
|
|
||||||
//! )
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! For clarity, the actual implementation in Bizinikiwi looks like this:
|
|
||||||
#![doc = docify::embed!("../../bizinikiwi/primitives/runtime/src/generic/unchecked_extrinsic.rs", unchecked_extrinsic_encode_impl)]
|
|
||||||
//!
|
|
||||||
//! Let's look at how each of these details is constructed:
|
|
||||||
//!
|
|
||||||
//! ## compact_encoded_length
|
|
||||||
//!
|
|
||||||
//! This is a [SCALE compact encoded][pezframe::deps::codec::Compact] integer which is equal to the
|
|
||||||
//! length, in bytes, of the rest of the extrinsic details.
|
|
||||||
//!
|
|
||||||
//! To obtain this value, we must encode and concatenate together the rest of the extrinsic details
|
|
||||||
//! first, and then obtain the byte length of these. We can then compact encode that length, and
|
|
||||||
//! prepend it to the rest of the details.
|
|
||||||
//!
|
|
||||||
//! ## version_and_maybe_signature
|
|
||||||
//!
|
|
||||||
//! If the extrinsic is _unsigned_, then `version_and_maybe_signature` will be just one byte
|
|
||||||
//! denoting the _transaction protocol version_, which is 4 (or `0b0000_0100`).
|
|
||||||
//!
|
|
||||||
//! If the extrinsic is _signed_ (all extrinsics submitted from users must be signed), then
|
|
||||||
//! `version_and_maybe_signature` is obtained by concatenating some details together, ie:
|
|
||||||
//!
|
|
||||||
//! ```text
|
|
||||||
//! version_and_maybe_signature = concat(
|
|
||||||
//! version_and_signed,
|
|
||||||
//! from_address,
|
|
||||||
//! signature,
|
|
||||||
//! transaction_extensions_extra,
|
|
||||||
//! )
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! Each of the details to be concatenated together is explained below:
|
|
||||||
//!
|
|
||||||
//! ## version_and_extrinsic_type
|
|
||||||
//!
|
|
||||||
//! This byte has 2 components:
|
|
||||||
//! - the 2 most significant bits represent the extrinsic type:
|
|
||||||
//! - bare - `0b00`
|
|
||||||
//! - signed - `0b10`
|
|
||||||
//! - general - `0b01`
|
|
||||||
//! - the 6 least significant bits represent the extrinsic format version (currently 5)
|
|
||||||
//!
|
|
||||||
//! ### Bare extrinsics
|
|
||||||
//!
|
|
||||||
//! If the extrinsic is _bare_, then `version_and_extrinsic_type` will be just the _transaction
|
|
||||||
//! protocol version_, which is 5 (or `0b0000_0101`). Bare extrinsics do not carry any other
|
|
||||||
//! extension data, so `maybe_extension_data` would not be included in the payload and the
|
|
||||||
//! `version_and_extrinsic_type` would always be followed by the encoded call bytes.
|
|
||||||
//!
|
|
||||||
//! ### Signed extrinsics
|
|
||||||
//!
|
|
||||||
//! If the extrinsic is _signed_ (all extrinsics submitted from users used to be signed up until
|
|
||||||
//! version 4), then `version_and_extrinsic_type` is obtained by having a MSB of `1` on the
|
|
||||||
//! _transaction protocol version_ byte (which translates to `0b1000_0101`).
|
|
||||||
//!
|
|
||||||
//! Additionally, _signed_ extrinsics also carry with them address and signature information encoded
|
|
||||||
//! as follows:
|
|
||||||
//!
|
|
||||||
//! #### from_address
|
|
||||||
//!
|
|
||||||
//! This is the [SCALE encoded][pezframe::deps::codec] address of the sender of the extrinsic. The
|
|
||||||
//! address is the first generic parameter of [`pezsp_runtime::generic::UncheckedExtrinsic`], and so
|
|
||||||
//! can vary from chain to chain.
|
|
||||||
//!
|
|
||||||
//! The address type used on the Pezkuwi relay chain is
|
|
||||||
//! [`pezsp_runtime::MultiAddress<AccountId32>`], where `AccountId32` is defined
|
|
||||||
//! [here][`pezsp_core::crypto::AccountId32`]. When constructing a signed extrinsic to be submitted
|
|
||||||
//! to a Pezkuwi node, you'll always use the [`pezsp_runtime::MultiAddress::Id`] variant to wrap
|
|
||||||
//! your `AccountId32`.
|
|
||||||
//!
|
|
||||||
//! #### signature
|
|
||||||
//!
|
|
||||||
//! This is the [SCALE encoded][pezframe::deps::codec] signature. The signature type is configured via
|
|
||||||
//! the third generic parameter of [`pezsp_runtime::generic::UncheckedExtrinsic`], which determines
|
|
||||||
//! the shape of the signature and signing algorithm that should be used.
|
|
||||||
//!
|
|
||||||
//! The signature is obtained by signing the _signed payload_ bytes (see below on how this is
|
|
||||||
//! constructed) using the private key associated with the address and correct algorithm.
|
|
||||||
//!
|
|
||||||
//! The signature type used on the Pezkuwi relay chain is [`pezsp_runtime::MultiSignature`]; the
|
|
||||||
//! variants there are the types of signature that can be provided.
|
|
||||||
//!
|
|
||||||
//! ### General extrinsics
|
|
||||||
//!
|
|
||||||
//! If the extrinsic is _general_ (it doesn't carry a signature in the payload, only extension
|
|
||||||
//! data), then `version_and_extrinsic_type` is obtained by logical OR between the general
|
|
||||||
//! transaction type bits and the _transaction protocol version_ byte (which translates to
|
|
||||||
//! `0b0100_0101`).
|
|
||||||
//!
|
|
||||||
//! ### transaction_extensions_extra
|
|
||||||
//!
|
|
||||||
//! This is the concatenation of the [SCALE encoded][pezframe::deps::codec] bytes representing first a
|
|
||||||
//! single byte describing the extension version (this is bumped whenever a change occurs in the
|
|
||||||
//! transaction extension pipeline) followed by the bytes of each of the [_transaction
|
|
||||||
//! extensions_][pezsp_runtime::traits::TransactionExtension], and are configured by the fourth
|
|
||||||
//! generic parameter of [`pezsp_runtime::generic::UncheckedExtrinsic`]. Learn more about
|
|
||||||
//! transaction extensions [here][crate::reference_docs::transaction_extensions].
|
|
||||||
//!
|
|
||||||
//! When it comes to constructing an extrinsic, each transaction extension has two things that we
|
|
||||||
//! are interested in here:
|
|
||||||
//!
|
|
||||||
//! - The actual SCALE encoding of the transaction extension type itself; this is what will form our
|
|
||||||
//! `transaction_extensions_extra` bytes.
|
|
||||||
//! - An `Implicit` type. This is SCALE encoded into the `transaction_extensions_implicit` data (see
|
|
||||||
//! below).
|
|
||||||
//!
|
|
||||||
//! Either (or both) of these can encode to zero bytes.
|
|
||||||
//!
|
|
||||||
//! Each chain configures the set of transaction extensions that it uses in its runtime
|
|
||||||
//! configuration. At the time of writing, Pezkuwi configures them
|
|
||||||
//! [here](https://github.com/polkadot-fellows/runtimes/blob/1dc04eb954eadf8aadb5d83990b89662dbb5a074/relay/polkadot/src/lib.rs#L1432C25-L1432C25).
|
|
||||||
//! Some of the common transaction extensions are defined
|
|
||||||
//! [here][pezframe::deps::pezframe_system#transaction-extensions].
|
|
||||||
//!
|
|
||||||
//! Information about exactly which transaction extensions are present on a chain and in what order
|
|
||||||
//! is also a part of the metadata for the chain. For V15 metadata, it can be [found
|
|
||||||
//! here][pezframe::deps::pezframe_support::__private::metadata::v15::ExtrinsicMetadata].
|
|
||||||
//!
|
|
||||||
//! ## call_data
|
|
||||||
//!
|
|
||||||
//! This is the main payload of the extrinsic, which is used to determine how the chain's state is
|
|
||||||
//! altered. This is defined by the second generic parameter of
|
|
||||||
//! [`pezsp_runtime::generic::UncheckedExtrinsic`].
|
|
||||||
//!
|
|
||||||
//! A call can be anything that implements [`Encode`][pezframe::deps::codec::Encode]. In FRAME-based
|
|
||||||
//! runtimes, a call is represented as an enum of enums, where the outer enum represents the FRAME
|
|
||||||
//! pezpallet being called, and the inner enum represents the call being made within that pezpallet,
|
|
||||||
//! and any arguments to it. Read more about the call enum
|
|
||||||
//! [here][crate::reference_docs::frame_runtime_types].
|
|
||||||
//!
|
|
||||||
//! FRAME `Call` enums are automatically generated, and end up looking something like this:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/extrinsic_encoding.rs", call_data)]
|
|
||||||
//!
|
|
||||||
//! In pseudo-code, this `Call` enum encodes equivalently to:
|
|
||||||
//!
|
|
||||||
//! ```text
|
|
||||||
//! call_data = concat(
|
|
||||||
//! pezpallet_index,
|
|
||||||
//! call_index,
|
|
||||||
//! call_args
|
|
||||||
//! )
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! - `pezpallet_index` is a single byte denoting the index of the pezpallet that we are calling
|
|
||||||
//! into, and is what the tag of the outermost enum will encode to.
|
|
||||||
//! - `call_index` is a single byte denoting the index of the call that we are making the pezpallet,
|
|
||||||
//! and is what the tag of the inner enum will encode to.
|
|
||||||
//! - `call_args` are the SCALE encoded bytes for each of the arguments that the call expects, and
|
|
||||||
//! are typically provided as values to the inner enum.
|
|
||||||
//!
|
|
||||||
//! Information about the pallets that exist for a chain (including their indexes), the calls
|
|
||||||
//! available in each pezpallet (including their indexes), and the arguments required for each call
|
|
||||||
//! can be found in the metadata for the chain. For V15 metadata, this information [is
|
|
||||||
//! here][pezframe::deps::pezframe_support::__private::metadata::v15::PalletMetadata].
|
|
||||||
//!
|
|
||||||
//! # The Signed Payload Format
|
|
||||||
//!
|
|
||||||
//! All _signed_ extrinsics submitted to a node from the outside world (also known as
|
|
||||||
//! _transactions_) need to be _signed_. The data that needs to be signed for some extrinsic is
|
|
||||||
//! called the _signed payload_, and its shape is described by the following pseudo-code:
|
|
||||||
//!
|
|
||||||
//! ```text
|
|
||||||
//! signed_payload = blake2_256(
|
|
||||||
//! concat(
|
|
||||||
//! call_data,
|
|
||||||
//! transaction_extensions_extra,
|
|
||||||
//! transaction_extensions_implicit,
|
|
||||||
//! )
|
|
||||||
//! )
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! The bytes representing `call_data` and `transaction_extensions_extra` can be obtained as
|
|
||||||
//! descibed above. `transaction_extensions_implicit` is constructed by SCALE encoding the
|
|
||||||
//! ["implicit" data][pezsp_runtime::traits::TransactionExtension::Implicit] for each transaction
|
|
||||||
//! extension that the chain is using, in order.
|
|
||||||
//!
|
|
||||||
//! Once we've concatenated those together, we hash the result using a Blake2 256bit hasher.
|
|
||||||
//!
|
|
||||||
//! The [`pezsp_runtime::generic::SignedPayload`] type takes care of assembling the correct payload
|
|
||||||
//! for us, given `call_data` and a tuple of transaction extensions.
|
|
||||||
//!
|
|
||||||
//! # The General Transaction Format
|
|
||||||
//!
|
|
||||||
//! A General transaction does not have a signature method hardcoded in the check logic of the
|
|
||||||
//! extrinsic, such as a traditionally signed transaction. Instead, general transactions should have
|
|
||||||
//! one or more extensions in the transaction extension pipeline that auhtorize origins in some way,
|
|
||||||
//! one of which could be the traditional signature check that happens for all signed transactions
|
|
||||||
//! in the [Checkable](pezsp_runtime::traits::Checkable) implementation of
|
|
||||||
//! [UncheckedExtrinsic](pezsp_runtime::generic::UncheckedExtrinsic). Therefore, it is up to each
|
|
||||||
//! extension to define the format of the payload it will try to check and authorize the right
|
|
||||||
//! origin type. For an example, look into the [authorization example pezpallet
|
|
||||||
//! extensions](pezpallet_example_authorization_tx_extension::extensions)
|
|
||||||
//!
|
|
||||||
//! # Example Encoding
|
|
||||||
//!
|
|
||||||
//! Using [`pezsp_runtime::generic::UncheckedExtrinsic`], we can construct and encode an extrinsic
|
|
||||||
//! as follows:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/extrinsic_encoding.rs", encoding_example)]
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
pub mod call_data {
|
|
||||||
use codec::{Decode, Encode};
|
|
||||||
use pezsp_runtime::{traits::Dispatchable, DispatchResultWithInfo};
|
|
||||||
|
|
||||||
// The outer enum composes calls within
|
|
||||||
// different pallets together. We have two
|
|
||||||
// pallets, "PalletA" and "PalletB".
|
|
||||||
#[derive(Encode, Decode, Clone)]
|
|
||||||
pub enum Call {
|
|
||||||
#[codec(index = 0)]
|
|
||||||
PalletA(PalletACall),
|
|
||||||
#[codec(index = 7)]
|
|
||||||
PalletB(PalletBCall),
|
|
||||||
}
|
|
||||||
|
|
||||||
// An inner enum represents the calls within
|
|
||||||
// a specific pezpallet. "PalletA" has one call,
|
|
||||||
// "Foo".
|
|
||||||
#[derive(Encode, Decode, Clone)]
|
|
||||||
pub enum PalletACall {
|
|
||||||
#[codec(index = 0)]
|
|
||||||
Foo(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Encode, Decode, Clone)]
|
|
||||||
pub enum PalletBCall {
|
|
||||||
#[codec(index = 0)]
|
|
||||||
Bar(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Dispatchable for Call {
|
|
||||||
type RuntimeOrigin = ();
|
|
||||||
type Config = ();
|
|
||||||
type Info = ();
|
|
||||||
type PostInfo = ();
|
|
||||||
fn dispatch(self, _origin: Self::RuntimeOrigin) -> DispatchResultWithInfo<Self::PostInfo> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
pub mod encoding_example {
|
|
||||||
use super::call_data::{Call, PalletACall};
|
|
||||||
use crate::reference_docs::transaction_extensions::transaction_extensions_example;
|
|
||||||
use codec::Encode;
|
|
||||||
use pezsp_core::crypto::AccountId32;
|
|
||||||
use pezsp_keyring::sr25519::Keyring;
|
|
||||||
use pezsp_runtime::{
|
|
||||||
generic::{SignedPayload, UncheckedExtrinsic},
|
|
||||||
MultiAddress, MultiSignature,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Define some transaction extensions to use. We'll use a couple of examples
|
|
||||||
// from the transaction extensions reference doc.
|
|
||||||
type TransactionExtensions = (
|
|
||||||
transaction_extensions_example::AddToPayload,
|
|
||||||
transaction_extensions_example::AddToSignaturePayload,
|
|
||||||
);
|
|
||||||
|
|
||||||
// We'll use `UncheckedExtrinsic` to encode our extrinsic for us. We set
|
|
||||||
// the address and signature type to those used on Pezkuwi, use our custom
|
|
||||||
// `Call` type, and use our custom set of `TransactionExtensions`.
|
|
||||||
type Extrinsic = UncheckedExtrinsic<
|
|
||||||
MultiAddress<AccountId32, ()>,
|
|
||||||
Call,
|
|
||||||
MultiSignature,
|
|
||||||
TransactionExtensions,
|
|
||||||
>;
|
|
||||||
|
|
||||||
pub fn encode_demo_extrinsic() -> Vec<u8> {
|
|
||||||
// The "from" address will be our Alice dev account.
|
|
||||||
let from_address = MultiAddress::<AccountId32, ()>::Id(Keyring::Alice.to_account_id());
|
|
||||||
|
|
||||||
// We provide some values for our expected transaction extensions.
|
|
||||||
let transaction_extensions = (
|
|
||||||
transaction_extensions_example::AddToPayload(1),
|
|
||||||
transaction_extensions_example::AddToSignaturePayload,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Construct our call data:
|
|
||||||
let call_data = Call::PalletA(PalletACall::Foo("Hello".to_string()));
|
|
||||||
|
|
||||||
// The signed payload. This takes care of encoding the call_data,
|
|
||||||
// transaction_extensions_extra and transaction_extensions_implicit, and hashing
|
|
||||||
// the result if it's > 256 bytes:
|
|
||||||
let signed_payload = SignedPayload::new(call_data.clone(), transaction_extensions.clone());
|
|
||||||
|
|
||||||
// Sign the signed payload with our Alice dev account's private key,
|
|
||||||
// and wrap the signature into the expected type:
|
|
||||||
let signature = {
|
|
||||||
let sig = Keyring::Alice.sign(&signed_payload.encode());
|
|
||||||
MultiSignature::Sr25519(sig)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Now, we can build and encode our extrinsic:
|
|
||||||
let ext = Extrinsic::new_signed(call_data, from_address, signature, transaction_extensions);
|
|
||||||
|
|
||||||
let encoded_ext = ext.encode();
|
|
||||||
encoded_ext
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
//! # Fee-Less Runtime
|
|
||||||
//!
|
|
||||||
//! 🚧 Work In Progress 🚧
|
|
||||||
//!
|
|
||||||
//! Notes:
|
|
||||||
//!
|
|
||||||
//! - An extension of [`runtime_vs_smart_contract`], showcasing the tools needed to build a safe
|
|
||||||
//! runtime that is fee-less.
|
|
||||||
//! - Would need to use unsigned origins, custom validate_unsigned, check the existence of some NFT
|
|
||||||
//! and some kind of rate limiting (eg. any account gets 5 free tx per day).
|
|
||||||
//! - The rule of thumb is that as long as the unsigned validate does one storage read, similar to
|
|
||||||
//! nonce, it is fine.
|
|
||||||
//! - This could possibly be a good guide/template, rather than a reference doc.
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
//! # FRAME Logging
|
|
||||||
//!
|
|
||||||
//! This reference docs briefly explores how to do logging and printing runtimes, mainly
|
|
||||||
//! FRAME-based.
|
|
||||||
//!
|
|
||||||
//! > Please make sure to read [the section below](#using-logging-in-production) on using logging in
|
|
||||||
//! > production.
|
|
||||||
//!
|
|
||||||
//! ## Using `println!`
|
|
||||||
//!
|
|
||||||
//! To recap, as with standard Rust, you can use `println!` _in your tests_, but it will only print
|
|
||||||
//! out if executed with `--nocapture`, or if the test panics.
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! fn it_print() {
|
|
||||||
//! println!("Hello, world!");
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! within the pezpallet, if you want to use the standard `println!`, it needs to be wrapped in
|
|
||||||
//! [`pezsp_std::if_std`]. Of course, this means that this print code is only available to you in
|
|
||||||
//! the `std` compiler flag, and never present in a wasm build.
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! // somewhere in your pezpallet. This is not a real pezpallet code.
|
|
||||||
//! mod pezpallet {
|
|
||||||
//! struct Pezpallet;
|
|
||||||
//! impl Pezpallet {
|
|
||||||
//! fn print() {
|
|
||||||
//! pezsp_std::if_std! {
|
|
||||||
//! println!("Hello, world!");
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! ## Using `log`
|
|
||||||
//!
|
|
||||||
//! First, ensure you are familiar with the [`log`] crate. In short, each log statement has:
|
|
||||||
//!
|
|
||||||
//! 1. `log-level`, signifying how important it is.
|
|
||||||
//! 2. `log-target`, signifying to which component it belongs.
|
|
||||||
//!
|
|
||||||
//! Add log statements to your pezpallet as such:
|
|
||||||
//!
|
|
||||||
//! You can add the log crate to the `Cargo.toml` of the pezpallet.
|
|
||||||
//!
|
|
||||||
//! ```text
|
|
||||||
//! #[dependencies]
|
|
||||||
//! log = { version = "x.y.z", default-features = false }
|
|
||||||
//!
|
|
||||||
//! #[features]
|
|
||||||
//! std = [
|
|
||||||
//! // snip -- other pallets
|
|
||||||
//! "log/std"
|
|
||||||
//! ]
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! More conveniently, the `frame` umbrella crate re-exports the log crate as [`pezframe::log`].
|
|
||||||
//!
|
|
||||||
//! Then, the pezpallet can use this crate to emit log statements. In this statement, we use the
|
|
||||||
//! info level, and the target is `pezpallet-example`.
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! mod pezpallet {
|
|
||||||
//! struct Pezpallet;
|
|
||||||
//!
|
|
||||||
//! impl Pezpallet {
|
|
||||||
//! fn logs() {
|
|
||||||
//! pezframe::log::info!(target: "pezpallet-example", "Hello, world!");
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! This will in itself just emit the log messages, **but unless if captured by a logger, they will
|
|
||||||
//! not go anywhere**. [`pezsp_api`] provides a handy function to enable the runtime logging:
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! // in your test
|
|
||||||
//! fn it_also_prints() {
|
|
||||||
//! pezsp_api::init_runtime_logger();
|
|
||||||
//! // call into your pezpallet, and now it will print `log` statements.
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! Alternatively, you can use [`pezsp_tracing::try_init_simple`].
|
|
||||||
//!
|
|
||||||
//! `info`, `error` and `warn` logs are printed by default, but if you want lower level logs to also
|
|
||||||
//! be printed, you must to add the following compiler flag:
|
|
||||||
//!
|
|
||||||
//! ```text
|
|
||||||
//! RUST_LOG=pezpallet-example=trace cargo test
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! ## Enabling Logs in Production
|
|
||||||
//!
|
|
||||||
//! All logs from the runtime are emitted by default, but there is a feature flag in [`pezsp_api`],
|
|
||||||
//! called `disable-logging`, that can be used to disable all logs in the runtime. This is useful
|
|
||||||
//! for production chains to reduce the size and overhead of the wasm runtime.
|
|
||||||
#![doc = docify::embed!("../../bizinikiwi/primitives/api/src/lib.rs", init_runtime_logger)]
|
|
||||||
//!
|
|
||||||
//! Similar to the above, the proper `RUST_LOG` must also be passed to your compiler flag when
|
|
||||||
//! compiling the runtime.
|
|
||||||
//!
|
|
||||||
//! ## Log Target Prefixing
|
|
||||||
//!
|
|
||||||
//! Many [`crate::pezkuwi_sdk::frame_runtime`] pallets emit logs with log target `runtime::<name of
|
|
||||||
//! pezpallet>`, for example `runtime::system`. This then allows one to run a node with a wasm blob
|
|
||||||
//! compiled with `LOG_TARGET=runtime=debug`, which enables the log target of all pallets who's log
|
|
||||||
//! target starts with `runtime`.
|
|
||||||
//!
|
|
||||||
//! ## Low Level Primitives
|
|
||||||
//!
|
|
||||||
//! Under the hood, logging is another instance of host functions under the hood (as defined in
|
|
||||||
//! [`crate::reference_docs::wasm_meta_protocol`]). The runtime uses a set of host functions under
|
|
||||||
//! [`pezsp_io::logging`] and [`pezsp_io::misc`] to emit all logs and prints. You typically do not
|
|
||||||
//! need to use these APIs directly.
|
|
||||||
//!
|
|
||||||
//! ## Using Logging in Production
|
|
||||||
//!
|
|
||||||
//! Note that within FRAME, reading storage values __only for the purpose of logging__ is dangerous,
|
|
||||||
//! and can lead to consensus issues. This is because with the introduction of
|
|
||||||
//! [`crate::guides::enable_pov_reclaim`], the node side code will track the storage changes, and
|
|
||||||
//! tries to update the onchain record of the `proof_size` weight used (stored in
|
|
||||||
//! [`pezframe_system::BlockWeight`]) after the block is executed.
|
|
||||||
//!
|
|
||||||
//! If one node has a different log level enabled than the rest of the network, and the extra logs
|
|
||||||
//! impose additional storage reads, then the amount of `proof_size` weight reclaimed into
|
|
||||||
//! [`pezframe_system::BlockWeight`] will be different, causing a state root mismatch, which is
|
|
||||||
//! typically a fatal error emitted from [`pezframe_executive`].
|
|
||||||
//!
|
|
||||||
//! This also can also happen in a teyrchain context, and cause discrepancies between the relay
|
|
||||||
//! chain and the teyrchain, when execution the Teyrchain Validation Function (PVF) on the relay
|
|
||||||
//! chain.
|
|
||||||
//!
|
|
||||||
//! **In summary, you should only used storage values in logging (especially for levels lower than
|
|
||||||
//! `info` which is typically enabled by all parties) that are already read from storage, and will
|
|
||||||
//! be part of the storage proof of execution in any case**.
|
|
||||||
//!
|
|
||||||
//! A typical faulty code would look like this:
|
|
||||||
//!
|
|
||||||
//! ```ignore
|
|
||||||
//! /// This function will have a different storage footprint depending on the log level
|
|
||||||
//! fn faulty_logging() {
|
|
||||||
//! log::debug!(
|
|
||||||
//! "what I am about to print is only read when `RUST_LOG=debug` {:?}",
|
|
||||||
//! StorageValue::<T>::get()
|
|
||||||
//! );
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! Please read [this issue](https://github.com/pezkuwichain/pezkuwi-sdk/issues/298) for one
|
|
||||||
//! instance of the consensus issues caused by this mistake.
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
//! # Offchain Workers
|
|
||||||
//!
|
|
||||||
//! This reference document explains how offchain workers work in Bizinikiwi and FRAME. The main
|
|
||||||
//! focus is upon FRAME's implementation of this functionality. Nonetheless, offchain workers are a
|
|
||||||
//! Bizinikiwi-provided feature and can be used with possible alternatives to [`frame`] as well.
|
|
||||||
//!
|
|
||||||
//! Offchain workers are a commonly misunderstood topic, therefore we explain them bottom-up,
|
|
||||||
//! starting at the fundamentals and then describing the developer interface.
|
|
||||||
//!
|
|
||||||
//! ## Context
|
|
||||||
//!
|
|
||||||
//! Recall from [`crate::reference_docs::wasm_meta_protocol`] that the node and the runtime
|
|
||||||
//! communicate with one another via host functions and runtime APIs. Many of these interactions
|
|
||||||
//! contribute to the actual state transition of the blockchain. For example [`pezsp_api::Core`] is
|
|
||||||
//! the main runtime API that is called to execute new blocks.
|
|
||||||
//!
|
|
||||||
//! Offchain workers are in principle not different in any way: It is a runtime API exposed by the
|
|
||||||
//! wasm blob ([`pezsp_offchain::OffchainWorkerApi`]), and the node software calls into it when it
|
|
||||||
//! deems fit. But, crucially, this API call is different in that:
|
|
||||||
//!
|
|
||||||
//! 1. It can have no impact on the state ie. it is _OFF (the) CHAIN_. If any state is altered
|
|
||||||
//! during the execution of this API call, it is discarded.
|
|
||||||
//! 2. It has access to an extended set of host functions that allow the wasm blob to do more. For
|
|
||||||
//! example, call into HTTP requests.
|
|
||||||
//!
|
|
||||||
//! > The main way through which an offchain worker can interact with the state is by submitting an
|
|
||||||
//! > extrinsic to the chain. This is the ONLY way to alter the state from an offchain worker.
|
|
||||||
//! > [`pezpallet_example_offchain_worker`] provides an example of this.
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//! Given the "Off Chain" nature of this API, it is important to remember that calling this API is
|
|
||||||
//! entirely optional. Some nodes might call into it, some might not, and it would have no impact on
|
|
||||||
//! the execution of your blockchain because no state is altered no matter the execution of the
|
|
||||||
//! offchain worker API.
|
|
||||||
//!
|
|
||||||
//! Bizinikiwi's CLI allows some degree of configuration about this, allowing node operators to
|
|
||||||
//! specify when they want to run the offchain worker API. See
|
|
||||||
//! [`pezsc_cli::RunCmd::offchain_worker_params`].
|
|
||||||
//!
|
|
||||||
//! ## Nondeterministic Execution
|
|
||||||
//!
|
|
||||||
//! Needless to say, given the above description, the code in your offchain worker API can be
|
|
||||||
//! nondeterministic, as it is not part of the blockchain's STF, so it can be executed at unknown
|
|
||||||
//! times, by unknown nodes, and has no impact on the state. This is why an HTTP
|
|
||||||
//! ([`pezsp_runtime::offchain::http`]) API is readily provided to the offchain worker APIs. Because
|
|
||||||
//! there is no need for determinism in this context.
|
|
||||||
//!
|
|
||||||
//! > A common mistake here is for novice developers to see this HTTP API, and imagine that
|
|
||||||
//! > `pezkuwi-sdk` somehow magically solved the determinism in blockchains, and now a blockchain
|
|
||||||
//! > can make HTTP calls and it will all work. This is absolutely NOT the case. An HTTP call made
|
|
||||||
//! > by the offchain worker is non-deterministic by design. Blockchains can't and always won't be
|
|
||||||
//! > able to perform non-deterministic operations such as making HTTP calls to a foreign server.
|
|
||||||
//!
|
|
||||||
//! ## FRAME's API
|
|
||||||
//!
|
|
||||||
//! [`frame`] provides a simple API through which pallets can define offchain worker functions. This
|
|
||||||
//! is part of [`pezframe::traits::Hooks`], which is implemented as a part of
|
|
||||||
//! [`pezframe::pezpallet_macros::hooks`].
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #[pezframe::pezpallet]
|
|
||||||
//! pub mod pezpallet {
|
|
||||||
//! use pezframe::prelude::*;
|
|
||||||
//!
|
|
||||||
//! #[pezpallet::config]
|
|
||||||
//! pub trait Config: pezframe_system::Config {}
|
|
||||||
//!
|
|
||||||
//! #[pezpallet::pezpallet]
|
|
||||||
//! pub struct Pezpallet<T>(_);
|
|
||||||
//!
|
|
||||||
//! #[pezpallet::hooks]
|
|
||||||
//! impl<T: Config> Hooks<BlockNumberFor<T>> for Pezpallet<T> {
|
|
||||||
//! fn offchain_worker(block_number: BlockNumberFor<T>) {
|
|
||||||
//! // ...
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! Additionally, [`pezsp_runtime::offchain`] provides a set of utilities that can be used to
|
|
||||||
//! moderate the execution of offchain workers.
|
|
||||||
//!
|
|
||||||
//! ## Think Twice: Why Use Bizinikiwi's Offchain Workers?
|
|
||||||
//!
|
|
||||||
//! Consider the fact that in principle, an offchain worker code written using the above API is no
|
|
||||||
//! different than an equivalent written with an _actual offchain interaction library_, such as
|
|
||||||
//! [Pezkuwi-JS](https://polkadot.js.org/docs/), or any of the other ones listed [here](https://github.com/substrate-developer-hub/awesome-substrate?tab=readme-ov-file#client-libraries).
|
|
||||||
//!
|
|
||||||
//! They can both read from the state, and have no means of updating the state, other than the route
|
|
||||||
//! of submitting an extrinsic to the chain. Therefore, it is worth thinking twice before embedding
|
|
||||||
//! a logic as a part of Bizinikiwi's offchain worker API. Does it have to be there? Can it not be a
|
|
||||||
//! simple, actual offchain application that lives outside of the chain's WASM blob?
|
|
||||||
//!
|
|
||||||
//! Some of the reasons why you might want to do the opposite, and actually embed an offchain worker
|
|
||||||
//! API into the WASM blob are:
|
|
||||||
//!
|
|
||||||
//! * Accessing the state is easier within the `offchain_worker` function, as it is already a part
|
|
||||||
//! of the runtime, and [`pezframe::pezpallet_macros::storage`] provides all the tools needed to read
|
|
||||||
//! the state. Other client libraries might provide varying degrees of capability here.
|
|
||||||
//! * It will be updated in synchrony with the runtime. A Bizinikiwi's offchain application is part
|
|
||||||
//! of the same WASM blob, and is therefore guaranteed to be up to date.
|
|
||||||
//!
|
|
||||||
//! For example, imagine you have modified a storage item to have a new type. This will possibly
|
|
||||||
//! require a [`crate::reference_docs::frame_runtime_upgrades_and_migrations`], and any offchain
|
|
||||||
//! code, such as a Pezkuwi-JS application, will have to be updated to reflect this change. Whereas
|
|
||||||
//! the WASM offchain worker code is guaranteed to already be updated, or else the runtime code will
|
|
||||||
//! not even compile.
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//! ## Further References
|
|
||||||
//!
|
|
||||||
//! - <https://forum.polkadot.network/t/offchain-workers-design-assumptions-vulnerabilities/2548>
|
|
||||||
//! - <https://exchange.pezkuwichain.app/questions/11058/how-can-i-create-ocw-that-wont-activates-every-block-but-will-activates-only-w/11060#11060>
|
|
||||||
//! - [Offchain worker example](https://github.com/pezkuwichain/pezkuwi-sdk/tree/main/bizinikiwi/pezframe/examples/offchain-worker)
|
|
||||||
//!
|
|
||||||
//! [`frame`]: crate::pezkuwi_sdk::frame_runtime
|
|
||||||
@@ -1,271 +0,0 @@
|
|||||||
//! # FRAME Origin
|
|
||||||
//!
|
|
||||||
//! Let's start by clarifying a common wrong assumption about Origin:
|
|
||||||
//!
|
|
||||||
//! **ORIGIN IS NOT AN ACCOUNT ID**.
|
|
||||||
//!
|
|
||||||
//! FRAME's origin abstractions allow you to convey meanings far beyond just an account-id being the
|
|
||||||
//! caller of an extrinsic. Nonetheless, an account-id having signed an extrinsic is one of the
|
|
||||||
//! meanings that an origin can convey. This is the commonly used
|
|
||||||
//! [`pezframe_system::ensure_signed`], where the return value happens to be an account-id.
|
|
||||||
//!
|
|
||||||
//! Instead, let's establish the following as the correct definition of an origin:
|
|
||||||
//!
|
|
||||||
//! > The origin type represents the privilege level of the caller of an extrinsic.
|
|
||||||
//!
|
|
||||||
//! That is, an extrinsic, through checking the origin, can *express what privilege level it wishes
|
|
||||||
//! to impose on the caller of the extrinsic*. One of those checks can be as simple as "*any account
|
|
||||||
//! that has signed a statement can pass*".
|
|
||||||
//!
|
|
||||||
//! But the origin system can also express more abstract and complicated privilege levels. For
|
|
||||||
//! example:
|
|
||||||
//!
|
|
||||||
//! * If the majority of token holders agreed upon this. This is more or less what the
|
|
||||||
//! [`pezpallet_democracy`] does under the hood ([reference](https://github.com/pezkuwichain/pezkuwi-sdk/blob/edd95b3749754d2ed0c5738588e872c87be91624/bizinikiwi/pezframe/democracy/src/lib.rs#L1603-L1633)).
|
|
||||||
//! * If a specific ratio of an instance of [`pezpallet_collective`]/DAO agrees upon this.
|
|
||||||
//! * If another consensus system, for example a bridged network or a teyrchain, agrees upon this.
|
|
||||||
//! * If the majority of validator/authority set agrees upon this[^1].
|
|
||||||
//! * If caller holds a particular NFT.
|
|
||||||
//!
|
|
||||||
//! and many more.
|
|
||||||
//!
|
|
||||||
//! ## Context
|
|
||||||
//!
|
|
||||||
//! First, let's look at where the `origin` type is encountered in a typical pezpallet. The `origin:
|
|
||||||
//! OriginFor<T>` has to be the first argument of any given callable extrinsic in FRAME:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", call_simple)]
|
|
||||||
//!
|
|
||||||
//! Typically, the code of an extrinsic starts with an origin check, such as
|
|
||||||
//! [`pezframe_system::ensure_signed`].
|
|
||||||
//!
|
|
||||||
//! Note that [`OriginFor`](pezframe_system::pezpallet_prelude::OriginFor) is merely a shorthand for
|
|
||||||
//! [`pezframe_system::Config::RuntimeOrigin`]. Given the name prefix `Runtime`, we can learn that
|
|
||||||
//! `RuntimeOrigin` is similar to `RuntimeCall` and others, a runtime composite enum that is
|
|
||||||
//! amalgamated at the runtime level. Read [`crate::reference_docs::frame_runtime_types`] to
|
|
||||||
//! familiarize yourself with these types.
|
|
||||||
//!
|
|
||||||
//! To understand this better, we will next create a pezpallet with a custom origin, which will add
|
|
||||||
//! a new variant to `RuntimeOrigin`.
|
|
||||||
//!
|
|
||||||
//! ## Adding Custom Pezpallet Origin to the Runtime
|
|
||||||
//!
|
|
||||||
//! For example, given a pezpallet that defines the following custom origin:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin)]
|
|
||||||
//!
|
|
||||||
//! And a runtime with the following pallets:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", runtime_exp)]
|
|
||||||
//!
|
|
||||||
//! The type [`crate::reference_docs::frame_origin::runtime_for_origin::RuntimeOrigin`] is expanded.
|
|
||||||
//! This `RuntimeOrigin` contains a variant for the [`pezframe_system::RawOrigin`] and the custom
|
|
||||||
//! origin of the pezpallet.
|
|
||||||
//!
|
|
||||||
//! > Notice how the [`pezframe_system::ensure_signed`] is nothing more than a `match` statement. If
|
|
||||||
//! > you want to know where the actual origin of an extrinsic is set (and the signature
|
|
||||||
//! > verification happens, if any), see
|
|
||||||
//! > [`pezsp_runtime::generic::CheckedExtrinsic#trait-implementations`], specifically
|
|
||||||
//! > [`pezsp_runtime::traits::Applyable`]'s implementation.
|
|
||||||
//!
|
|
||||||
//! ## Asserting on a Custom Internal Origin
|
|
||||||
//!
|
|
||||||
//! In order to assert on a custom origin that is defined within your pezpallet, we need a way to
|
|
||||||
//! first convert the `<T as pezframe_system::Config>::RuntimeOrigin` into the local `enum Origin`
|
|
||||||
//! of the current pezpallet. This is a common process that is explained in
|
|
||||||
//! [`crate::reference_docs::frame_runtime_types#
|
|
||||||
//! adding-further-constraints-to-runtime-composite-enums`].
|
|
||||||
//!
|
|
||||||
//! We use the same process here to express that `RuntimeOrigin` has a number of additional bounds,
|
|
||||||
//! as follows.
|
|
||||||
//!
|
|
||||||
//! 1. Defining a custom `RuntimeOrigin` with further bounds in the pezpallet.
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin_bound)]
|
|
||||||
//!
|
|
||||||
//! 2. Using it in the pezpallet.
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin_usage)]
|
|
||||||
//!
|
|
||||||
//! ## Asserting on a Custom External Origin
|
|
||||||
//!
|
|
||||||
//! Very often, a pezpallet wants to have a parameterized origin that is **NOT** defined within the
|
|
||||||
//! pezpallet. In other words, a pezpallet wants to delegate an origin check to something that is
|
|
||||||
//! specified later at the runtime level. Like many other parameterizations in FRAME, this implies
|
|
||||||
//! adding a new associated type to `trait Config`.
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_def)]
|
|
||||||
//!
|
|
||||||
//! Then, within the pezpallet, we can simply use this "unknown" origin check type:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_usage)]
|
|
||||||
//!
|
|
||||||
//! Finally, at the runtime, any implementation of [`pezframe::traits::EnsureOrigin`] can be passed.
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_provide)]
|
|
||||||
//!
|
|
||||||
//! Indeed, some of these implementations of [`pezframe::traits::EnsureOrigin`] are similar to the ones
|
|
||||||
//! that we know about: [`pezframe::runtime::prelude::EnsureSigned`],
|
|
||||||
//! [`pezframe::runtime::prelude::EnsureSignedBy`], [`pezframe::runtime::prelude::EnsureRoot`],
|
|
||||||
//! [`pezframe::runtime::prelude::EnsureNone`], etc. But, there are also many more that are not known
|
|
||||||
//! to us, and are defined in other pallets.
|
|
||||||
//!
|
|
||||||
//! For example, [`pezpallet_collective`] defines [`pezpallet_collective::EnsureMember`] and
|
|
||||||
//! [`pezpallet_collective::EnsureProportionMoreThan`] and many more, which is exactly what we
|
|
||||||
//! alluded to earlier in this document.
|
|
||||||
//!
|
|
||||||
//! Make sure to check the full list of [implementors of
|
|
||||||
//! `EnsureOrigin`](pezframe::traits::EnsureOrigin#implementors) for more inspiration.
|
|
||||||
//!
|
|
||||||
//! ## Obtaining Abstract Origins
|
|
||||||
//!
|
|
||||||
//! So far we have learned that FRAME pallets can assert on custom and abstract origin types,
|
|
||||||
//! whether they are defined within the pezpallet or not. But how can we obtain these abstract
|
|
||||||
//! origins?
|
|
||||||
//!
|
|
||||||
//! > All extrinsics that come from the outer world can generally only be obtained as either
|
|
||||||
//! > `signed` or `none` origin.
|
|
||||||
//!
|
|
||||||
//! Generally, these abstract origins are only obtained within the runtime, when a call is
|
|
||||||
//! dispatched within the runtime.
|
|
||||||
//!
|
|
||||||
//! ## Further References
|
|
||||||
//!
|
|
||||||
//! - [Gavin Wood's speech about FRAME features at Protocol Berg 2023.](https://youtu.be/j7b8Upipmeg?si=83_XUgYuJxMwWX4g&t=195)
|
|
||||||
//! - [A related StackExchange question.](https://exchange.pezkuwichain.app/questions/10992/how-do-you-find-the-public-key-for-the-medium-spender-track-origin)
|
|
||||||
//!
|
|
||||||
//! [^1]: Inherents are essentially unsigned extrinsics that need an [`pezframe_system::ensure_none`]
|
|
||||||
//! origin check, and through the virtue of being an inherent, are agreed upon by all validators.
|
|
||||||
|
|
||||||
use pezframe::prelude::*;
|
|
||||||
|
|
||||||
#[pezframe::pezpallet(dev_mode)]
|
|
||||||
pub mod pezpallet_for_origin {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[pezpallet::config]
|
|
||||||
pub trait Config: pezframe_system::Config {}
|
|
||||||
|
|
||||||
#[pezpallet::pezpallet]
|
|
||||||
pub struct Pezpallet<T>(_);
|
|
||||||
|
|
||||||
#[docify::export(call_simple)]
|
|
||||||
#[pezpallet::call]
|
|
||||||
impl<T: Config> Pezpallet<T> {
|
|
||||||
pub fn do_something(_origin: OriginFor<T>) -> DispatchResult {
|
|
||||||
// ^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pezframe::pezpallet(dev_mode)]
|
|
||||||
pub mod pezpallet_with_custom_origin {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[docify::export(custom_origin_bound)]
|
|
||||||
#[pezpallet::config]
|
|
||||||
pub trait Config: pezframe_system::Config {
|
|
||||||
type RuntimeOrigin: From<<Self as pezframe_system::Config>::RuntimeOrigin>
|
|
||||||
+ Into<Result<Origin, <Self as Config>::RuntimeOrigin>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pezpallet::pezpallet]
|
|
||||||
pub struct Pezpallet<T>(_);
|
|
||||||
|
|
||||||
#[docify::export(custom_origin)]
|
|
||||||
/// A dummy custom origin.
|
|
||||||
#[pezpallet::origin]
|
|
||||||
#[derive(
|
|
||||||
PartialEq,
|
|
||||||
Eq,
|
|
||||||
Clone,
|
|
||||||
RuntimeDebug,
|
|
||||||
Encode,
|
|
||||||
Decode,
|
|
||||||
DecodeWithMemTracking,
|
|
||||||
TypeInfo,
|
|
||||||
MaxEncodedLen,
|
|
||||||
)]
|
|
||||||
pub enum Origin {
|
|
||||||
/// If all holders of a particular NFT have agreed upon this.
|
|
||||||
AllNftHolders,
|
|
||||||
/// If all validators have agreed upon this.
|
|
||||||
ValidatorSet,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export(custom_origin_usage)]
|
|
||||||
#[pezpallet::call]
|
|
||||||
impl<T: Config> Pezpallet<T> {
|
|
||||||
pub fn only_validators(origin: OriginFor<T>) -> DispatchResult {
|
|
||||||
// first, we convert from `<T as pezframe_system::Config>::RuntimeOrigin` to `<T as
|
|
||||||
// Config>::RuntimeOrigin`
|
|
||||||
let local_runtime_origin = <<T as Config>::RuntimeOrigin as From<
|
|
||||||
<T as pezframe_system::Config>::RuntimeOrigin,
|
|
||||||
>>::from(origin);
|
|
||||||
// then we convert to `origin`, if possible
|
|
||||||
let local_origin =
|
|
||||||
local_runtime_origin.into().map_err(|_| "invalid origin type provided")?;
|
|
||||||
ensure!(matches!(local_origin, Origin::ValidatorSet), "Not authorized");
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod runtime_for_origin {
|
|
||||||
use super::pezpallet_with_custom_origin;
|
|
||||||
use pezframe::{runtime::prelude::*, testing_prelude::*};
|
|
||||||
|
|
||||||
#[docify::export(runtime_exp)]
|
|
||||||
construct_runtime!(
|
|
||||||
pub struct Runtime {
|
|
||||||
System: pezframe_system,
|
|
||||||
PalletWithCustomOrigin: pezpallet_with_custom_origin,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
|
|
||||||
impl pezframe_system::Config for Runtime {
|
|
||||||
type Block = MockBlock<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pezpallet_with_custom_origin::Config for Runtime {
|
|
||||||
type RuntimeOrigin = RuntimeOrigin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pezframe::pezpallet(dev_mode)]
|
|
||||||
pub mod pezpallet_with_external_origin {
|
|
||||||
use super::*;
|
|
||||||
#[docify::export(external_origin_def)]
|
|
||||||
#[pezpallet::config]
|
|
||||||
pub trait Config: pezframe_system::Config {
|
|
||||||
type ExternalOrigin: EnsureOrigin<Self::RuntimeOrigin>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pezpallet::pezpallet]
|
|
||||||
pub struct Pezpallet<T>(_);
|
|
||||||
|
|
||||||
#[docify::export(external_origin_usage)]
|
|
||||||
#[pezpallet::call]
|
|
||||||
impl<T: Config> Pezpallet<T> {
|
|
||||||
pub fn externally_checked_ext(origin: OriginFor<T>) -> DispatchResult {
|
|
||||||
T::ExternalOrigin::ensure_origin(origin)?;
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod runtime_for_external_origin {
|
|
||||||
use super::*;
|
|
||||||
use pezframe::{runtime::prelude::*, testing_prelude::*};
|
|
||||||
|
|
||||||
construct_runtime!(
|
|
||||||
pub struct Runtime {
|
|
||||||
System: pezframe_system,
|
|
||||||
PalletWithExternalOrigin: pezpallet_with_external_origin,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
|
|
||||||
impl pezframe_system::Config for Runtime {
|
|
||||||
type Block = MockBlock<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export(external_origin_provide)]
|
|
||||||
impl pezpallet_with_external_origin::Config for Runtime {
|
|
||||||
type ExternalOrigin = EnsureSigned<<Self as pezframe_system::Config>::AccountId>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,299 +0,0 @@
|
|||||||
//! # FRAME Pezpallet Coupling
|
|
||||||
//!
|
|
||||||
//! This reference document explains how FRAME pallets can be combined to interact together.
|
|
||||||
//!
|
|
||||||
//! It is suggested to re-read [`crate::pezkuwi_sdk::frame_runtime`], notably the information
|
|
||||||
//! around [`pezframe::pezpallet_macros::config`]. Recall that:
|
|
||||||
//!
|
|
||||||
//! > Configuration trait of a pezpallet: It allows a pezpallet to receive types at a later
|
|
||||||
//! > point from the runtime that wishes to contain it. It allows the pezpallet to be parameterized
|
|
||||||
//! > over both types and values.
|
|
||||||
//!
|
|
||||||
//! ## Context, Background
|
|
||||||
//!
|
|
||||||
//! FRAME pallets, as per described in [`crate::pezkuwi_sdk::frame_runtime`] are:
|
|
||||||
//!
|
|
||||||
//! > A pezpallet is a unit of encapsulated logic. It has a clearly defined responsibility and can
|
|
||||||
//! > be
|
|
||||||
//! linked to other pallets.
|
|
||||||
//!
|
|
||||||
//! That is to say:
|
|
||||||
//!
|
|
||||||
//! * *encapsulated*: Ideally, a FRAME pezpallet contains encapsulated logic which has clear
|
|
||||||
//! boundaries. It is generally a bad idea to build a single monolithic pezpallet that does
|
|
||||||
//! multiple things, such as handling currencies, identities and staking all at the same time.
|
|
||||||
//! * *linked to other pallets*: But, adhering extensively to the above also hinders the ability to
|
|
||||||
//! write useful applications. Pallets often need to work with each other, communicate and use
|
|
||||||
//! each other's functionalities.
|
|
||||||
//!
|
|
||||||
//! The broad principle that allows pallets to be linked together is the same way through which a
|
|
||||||
//! pezpallet uses its `Config` trait to receive types and values from the runtime that contains it.
|
|
||||||
//!
|
|
||||||
//! There are generally two ways to achieve this:
|
|
||||||
//!
|
|
||||||
//! 1. Tight coupling pallets.
|
|
||||||
//! 2. Loose coupling pallets.
|
|
||||||
//!
|
|
||||||
//! To explain the difference between the two, consider two pallets, `A` and `B`. In both cases, `A`
|
|
||||||
//! wants to use some functionality exposed by `B`.
|
|
||||||
//!
|
|
||||||
//! When tightly coupling pallets, `A` can only exist in a runtime if `B` is also present in the
|
|
||||||
//! same runtime. That is, `A` is expressing that can only work if `B` is present.
|
|
||||||
//!
|
|
||||||
//! This translates to the following Rust code:
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! trait Pallet_B_Config {}
|
|
||||||
//! trait Pallet_A_Config: Pallet_B_Config {}
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! Contrary, when pallets are loosely coupled, `A` expresses that some functionality, expressed via
|
|
||||||
//! a trait `F`, needs to be fulfilled. This trait is then implemented by `B`, and the two pallets
|
|
||||||
//! are linked together at the runtime level. This means that `A` only relies on the implementation
|
|
||||||
//! of `F`, which may be `B`, or another implementation of `F`.
|
|
||||||
//!
|
|
||||||
//! This translates to the following Rust code:
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! trait F {}
|
|
||||||
//! trait Pallet_A_Config {
|
|
||||||
//! type F: F;
|
|
||||||
//! }
|
|
||||||
//! // Pallet_B will implement and fulfill `F`.
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! ## Example
|
|
||||||
//!
|
|
||||||
//! Consider the following example, in which `pezpallet-foo` needs another pezpallet to provide the
|
|
||||||
//! block author to it, and `pezpallet-author` which has access to this information.
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", pezpallet_foo)]
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", pezpallet_author)]
|
|
||||||
//!
|
|
||||||
//! ### Tight Coupling Pallets
|
|
||||||
//!
|
|
||||||
//! To tightly couple `pezpallet-foo` and `pezpallet-author`, we use Rust's supertrait system. When
|
|
||||||
//! a pezpallet makes its own `trait Config` be bounded by another pezpallet's `trait Config`, it is
|
|
||||||
//! expressing two things:
|
|
||||||
//!
|
|
||||||
//! 1. That it can only exist in a runtime if the other pezpallet is also present.
|
|
||||||
//! 2. That it can use the other pezpallet's functionality.
|
|
||||||
//!
|
|
||||||
//! `pezpallet-foo`'s `Config` would then look like:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", tight_config)]
|
|
||||||
//!
|
|
||||||
//! And `pezpallet-foo` can use the method exposed by `pezpallet_author::Pezpallet` directly:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", tight_usage)]
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//! ### Loosely Coupling Pallets
|
|
||||||
//!
|
|
||||||
//! If `pezpallet-foo` wants to *not* rely on `pezpallet-author` directly, it can leverage its
|
|
||||||
//! `Config`'s associated types. First, we need a trait to express the functionality that
|
|
||||||
//! `pezpallet-foo` wants to obtain:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", AuthorProvider)]
|
|
||||||
//!
|
|
||||||
//! > We sometimes refer to such traits that help two pallets interact as "glue traits".
|
|
||||||
//!
|
|
||||||
//! Next, `pezpallet-foo` states that it needs this trait to be provided to it, at the runtime
|
|
||||||
//! level, via an associated type:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", loose_config)]
|
|
||||||
//!
|
|
||||||
//! Then, `pezpallet-foo` can use this trait to obtain the block author, without knowing where it
|
|
||||||
//! comes from:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", loose_usage)]
|
|
||||||
//!
|
|
||||||
//! Then, if `pezpallet-author` implements this glue-trait:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", pezpallet_author_provider)]
|
|
||||||
//!
|
|
||||||
//! And upon the creation of the runtime, the two pallets are linked together as such:
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", runtime_author_provider)]
|
|
||||||
//!
|
|
||||||
//! Crucially, when using loose coupling, we gain the flexibility of providing different
|
|
||||||
//! implementations of `AuthorProvider`, such that different users of a `pezpallet-foo` can use
|
|
||||||
//! different ones, without any code change being needed. For example, in the code snippets of this
|
|
||||||
//! module, you can find [`OtherAuthorProvider`], which is an alternative implementation of
|
|
||||||
//! [`AuthorProvider`].
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", other_author_provider)]
|
|
||||||
//!
|
|
||||||
//! A common pattern in pezkuwi-sdk is to provide an implementation of such glu traits for the unit
|
|
||||||
//! type as a "default/test behavior".
|
|
||||||
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", unit_author_provider)]
|
|
||||||
//!
|
|
||||||
//! ## Frame System
|
|
||||||
//!
|
|
||||||
//! With the above information in context, we can conclude that **`pezframe_system` is a special
|
|
||||||
//! pezpallet that is tightly coupled with every other pezpallet**. This is because it provides the
|
|
||||||
//! fundamental system functionality that every pezpallet needs, such as some types like
|
|
||||||
//! [`pezframe::prelude::pezframe_system::Config::AccountId`],
|
|
||||||
//! [`pezframe::prelude::pezframe_system::Config::Hash`], and some functionality such as block number,
|
|
||||||
//! etc.
|
|
||||||
//!
|
|
||||||
//! ## Recap
|
|
||||||
//!
|
|
||||||
//! To recap, consider the following rules of thumb:
|
|
||||||
//!
|
|
||||||
//! * In all cases, try and break down big pallets apart with clear boundaries of responsibility. In
|
|
||||||
//! general, it is easier to argue about multiple pezpallet if they only communicate together via
|
|
||||||
//! a known trait, rather than having access to all of each others public items, such as storage
|
|
||||||
//! and dispatchables.
|
|
||||||
//! * If a group of pallets is meant to work together, but is not foreseen to be generalized, or
|
|
||||||
//! used by others, consider tightly coupling pallets, *if it simplifies the development*.
|
|
||||||
//! * If a pezpallet needs a functionality provided by another pezpallet, but multiple
|
|
||||||
//! implementations can be foreseen, consider loosely coupling pallets.
|
|
||||||
//!
|
|
||||||
//! For example, all pallets in `pezkuwi-sdk` that needed to work with currencies could have been
|
|
||||||
//! tightly coupled with [`pezpallet_balances`]. But, `pezkuwi-sdk` also provides
|
|
||||||
//! [`pezpallet_assets`] (and more implementations by the community), therefore all pallets use
|
|
||||||
//! traits to loosely couple with balances or assets pezpallet. More on this in
|
|
||||||
//! [`crate::reference_docs::frame_tokens`].
|
|
||||||
//!
|
|
||||||
//! ## Further References
|
|
||||||
//!
|
|
||||||
//! - <https://www.youtube.com/watch?v=0eNGZpNkJk4>
|
|
||||||
//! - <https://exchange.pezkuwichain.app/questions/922/pezpallet-loose-couplingtight-coupling-and-missing-traits>
|
|
||||||
//!
|
|
||||||
//! [`AuthorProvider`]: crate::reference_docs::frame_pallet_coupling::AuthorProvider
|
|
||||||
//! [`OtherAuthorProvider`]: crate::reference_docs::frame_pallet_coupling::OtherAuthorProvider
|
|
||||||
|
|
||||||
#![allow(unused)]
|
|
||||||
|
|
||||||
use pezframe::prelude::*;
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[pezframe::pezpallet]
|
|
||||||
pub mod pezpallet_foo {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[pezpallet::config]
|
|
||||||
pub trait Config: pezframe_system::Config {}
|
|
||||||
|
|
||||||
#[pezpallet::pezpallet]
|
|
||||||
pub struct Pezpallet<T>(_);
|
|
||||||
|
|
||||||
impl<T: Config> Pezpallet<T> {
|
|
||||||
fn do_stuff_with_author() {
|
|
||||||
// needs block author here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
#[pezframe::pezpallet]
|
|
||||||
pub mod pezpallet_author {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[pezpallet::config]
|
|
||||||
pub trait Config: pezframe_system::Config {}
|
|
||||||
|
|
||||||
#[pezpallet::pezpallet]
|
|
||||||
pub struct Pezpallet<T>(_);
|
|
||||||
|
|
||||||
impl<T: Config> Pezpallet<T> {
|
|
||||||
pub fn author() -> T::AccountId {
|
|
||||||
todo!("somehow has access to the block author and can return it here")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pezframe::pezpallet]
|
|
||||||
pub mod pezpallet_foo_tight {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[pezpallet::pezpallet]
|
|
||||||
pub struct Pezpallet<T>(_);
|
|
||||||
|
|
||||||
#[docify::export(tight_config)]
|
|
||||||
/// This pezpallet can only live in a runtime that has both `pezframe_system` and
|
|
||||||
/// `pezpallet_author`.
|
|
||||||
#[pezpallet::config]
|
|
||||||
pub trait Config: pezframe_system::Config + pezpallet_author::Config {}
|
|
||||||
|
|
||||||
#[docify::export(tight_usage)]
|
|
||||||
impl<T: Config> Pezpallet<T> {
|
|
||||||
// anywhere in `pezpallet-foo`, we can call into `pezpallet-author` directly, namely because
|
|
||||||
// `T: pezpallet_author::Config`
|
|
||||||
fn do_stuff_with_author() {
|
|
||||||
let _ = pezpallet_author::Pezpallet::<T>::author();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export]
|
|
||||||
/// Abstraction over "something that can provide the block author".
|
|
||||||
pub trait AuthorProvider<AccountId> {
|
|
||||||
fn author() -> AccountId;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pezframe::pezpallet]
|
|
||||||
pub mod pezpallet_foo_loose {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[pezpallet::pezpallet]
|
|
||||||
pub struct Pezpallet<T>(_);
|
|
||||||
|
|
||||||
#[docify::export(loose_config)]
|
|
||||||
#[pezpallet::config]
|
|
||||||
pub trait Config: pezframe_system::Config {
|
|
||||||
/// This pezpallet relies on the existence of something that implements [`AuthorProvider`],
|
|
||||||
/// which may or may not be `pezpallet-author`.
|
|
||||||
type AuthorProvider: AuthorProvider<Self::AccountId>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export(loose_usage)]
|
|
||||||
impl<T: Config> Pezpallet<T> {
|
|
||||||
fn do_stuff_with_author() {
|
|
||||||
let _ = T::AuthorProvider::author();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export(pezpallet_author_provider)]
|
|
||||||
impl<T: pezpallet_author::Config> AuthorProvider<T::AccountId> for pezpallet_author::Pezpallet<T> {
|
|
||||||
fn author() -> T::AccountId {
|
|
||||||
pezpallet_author::Pezpallet::<T>::author()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct OtherAuthorProvider;
|
|
||||||
|
|
||||||
#[docify::export(other_author_provider)]
|
|
||||||
impl<AccountId> AuthorProvider<AccountId> for OtherAuthorProvider {
|
|
||||||
fn author() -> AccountId {
|
|
||||||
todo!("somehow get the block author here")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[docify::export(unit_author_provider)]
|
|
||||||
impl<AccountId> AuthorProvider<AccountId> for () {
|
|
||||||
fn author() -> AccountId {
|
|
||||||
todo!("somehow get the block author here")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod runtime {
|
|
||||||
use super::*;
|
|
||||||
use pezcumulus_pezpallet_aura_ext::pezpallet;
|
|
||||||
use pezframe::{runtime::prelude::*, testing_prelude::*};
|
|
||||||
|
|
||||||
construct_runtime!(
|
|
||||||
pub struct Runtime {
|
|
||||||
System: pezframe_system,
|
|
||||||
PalletFoo: pezpallet_foo_loose,
|
|
||||||
PalletAuthor: pezpallet_author,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
|
|
||||||
impl pezframe_system::Config for Runtime {
|
|
||||||
type Block = MockBlock<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pezpallet_author::Config for Runtime {}
|
|
||||||
|
|
||||||
#[docify::export(runtime_author_provider)]
|
|
||||||
impl pezpallet_foo_loose::Config for Runtime {
|
|
||||||
type AuthorProvider = pezpallet_author::Pezpallet<Runtime>;
|
|
||||||
// which is also equivalent to
|
|
||||||
// type AuthorProvider = PalletAuthor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||