diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b5dbd7e0c9..e2f9050358 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,6 +19,9 @@ variables: # No need for TLS because everything runs on the same machine DOCKER_TLS_CERTDIR: "" + + NIX_SHELL: "nix-shell" + stages: - environment - prebuild @@ -29,9 +32,9 @@ stages: before_script: - . /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh || true - - nix-shell --run "poetry install --remove-untracked" - - export LEGACY_VERSION=$(nix-shell --run "./tools/version.sh legacy/firmware/version.h") - - export CORE_VERSION=$(nix-shell --run "./tools/version.sh core/embed/firmware/version.h") + - $NIX_SHELL --run "poetry install --remove-untracked" + - export LEGACY_VERSION=$($NIX_SHELL --run "./tools/version.sh legacy/firmware/version.h") + - export CORE_VERSION=$($NIX_SHELL --run "./tools/version.sh core/embed/firmware/version.h") include: - ci/environment.yml diff --git a/README.md b/README.md index 627e8e5a29..400b6fbfd2 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Also please have a look at the docs, either in the `docs` folder or at [docs.tr ## Security vulnerability disclosure -Please report suspected security vulnerabilities in private to [security@satoshilabs.com](mailto:security@satoshilabs.com), also see [the disclosure section on the Trezor.io website](https://trezor.io/security/). Please do NOT create publicly viewable issues for suspected security vulnerabilities. +Please report suspected security vulnerabilities in private to [security@satoshilabs.com](mailto:security@satoshilabs.com), also see [the disclosure section on the Trezor.io website](https://trezor.io/support/a/how-to-report-a-security-issue). Please do NOT create publicly viewable issues for suspected security vulnerabilities. ## Documentation diff --git a/build-docker.sh b/build-docker.sh index 52ae60b624..4ab13fe536 100755 --- a/build-docker.sh +++ b/build-docker.sh @@ -32,6 +32,7 @@ if [ -z "$ALPINE_CHECKSUM" ]; then fi +DOCKER=${DOCKER:-docker} CONTAINER_NAME=${CONTAINER_NAME:-trezor-firmware-env.nix} ALPINE_CDN=${ALPINE_CDN:-https://dl-cdn.alpinelinux.org/alpine} ALPINE_RELEASE=${ALPINE_RELEASE:-3.15} @@ -137,7 +138,13 @@ echo echo ">>> DOCKER BUILD ALPINE_VERSION=$ALPINE_VERSION ALPINE_ARCH=$ALPINE_ARCH NIX_VERSION=$NIX_VERSION -t $CONTAINER_NAME" echo -docker build --build-arg ALPINE_VERSION="$ALPINE_VERSION" --build-arg ALPINE_ARCH="$ALPINE_ARCH" --build-arg NIX_VERSION="$NIX_VERSION" -t "$CONTAINER_NAME" ci/ +$DOCKER build \ + --network=host \ + --build-arg ALPINE_VERSION="$ALPINE_VERSION" \ + --build-arg ALPINE_ARCH="$ALPINE_ARCH" \ + --build-arg NIX_VERSION="$NIX_VERSION" \ + -t "$CONTAINER_NAME" \ + ci/ # stat under macOS has slightly different cli interface USER=$(stat -c "%u" . 2>/dev/null || stat -f "%u" .) @@ -179,7 +186,10 @@ EOF echo ">>> DOCKER RUN core BITCOIN_ONLY=$BITCOIN_ONLY PRODUCTION=$PRODUCTION" echo - docker run -it --rm \ + $DOCKER run \ + --network=host \ + -it \ + --rm \ -v "$DIR:/local" \ -v "$DIR/build/core$DIRSUFFIX":/build:z \ --env BITCOIN_ONLY="$BITCOIN_ONLY" \ @@ -225,7 +235,10 @@ EOF echo ">>> DOCKER RUN legacy BITCOIN_ONLY=$BITCOIN_ONLY PRODUCTION=$PRODUCTION" echo - docker run -it --rm \ + $DOCKER run \ + --network=host \ + -it \ + --rm \ -v "$DIR:/local" \ -v "$DIR/build/legacy$DIRSUFFIX":/build:z \ --env BITCOIN_ONLY="$BITCOIN_ONLY" \ @@ -233,7 +246,6 @@ EOF --init \ "$CONTAINER_NAME" \ /nix/var/nix/profiles/default/bin/nix-shell --run "bash /local/build/$SCRIPT_NAME" - done # all built, show fingerprints diff --git a/ci/build.yml b/ci/build.yml index d15929d747..edd0c245ba 100644 --- a/ci/build.yml +++ b/ci/build.yml @@ -22,17 +22,17 @@ core fw regular build: <<: *gitlab_caching needs: [] script: - - nix-shell --run "poetry run make -C core build_boardloader" - - nix-shell --run "poetry run make -C core build_bootloader" - - nix-shell --run "poetry run make -C core build_bootloader_ci" - - nix-shell --run "poetry run make -C core build_prodtest" - - nix-shell --run "poetry run make -C core build_firmware" - - nix-shell --run "poetry run make -C core sizecheck" - - cp core/build/firmware/firmware.bin trezor-fw-regular-$CORE_VERSION-$CI_COMMIT_SHORT_SHA.bin + - $NIX_SHELL --run "poetry run make -C core build_boardloader" + - $NIX_SHELL --run "poetry run make -C core build_bootloader" + - $NIX_SHELL --run "poetry run make -C core build_bootloader_ci" + - $NIX_SHELL --run "poetry run make -C core build_prodtest" + - $NIX_SHELL --run "poetry run make -C core build_firmware" + - $NIX_SHELL --run "poetry run make -C core sizecheck" + - cp core/build/firmware/firmware.bin firmware-T2T1-$CORE_VERSION-$CI_COMMIT_SHORT_SHA.bin artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: - - trezor-fw-regular-*.*.*-$CI_COMMIT_SHORT_SHA.bin + - firmware-T2T1-*.*.*-$CI_COMMIT_SHORT_SHA.bin expire_in: 1 week # Build of Core into firmware with enabled _debug_ mode. In debug mode you can @@ -43,12 +43,12 @@ core fw regular debug build: <<: *gitlab_caching needs: [] script: - - nix-shell --run "PYOPT=0 poetry run make -C core build_firmware" - - cp core/build/firmware/firmware.bin trezor-fw-regular-debug-$CORE_VERSION-$CI_COMMIT_SHORT_SHA.bin + - $NIX_SHELL --run "PYOPT=0 poetry run make -C core build_firmware" + - cp core/build/firmware/firmware.bin firmware-T2T1-debug-$CORE_VERSION-$CI_COMMIT_SHORT_SHA.bin artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: - - trezor-fw-regular-debug-*.*.*-$CI_COMMIT_SHORT_SHA.bin + - firmware-T2T1-debug-*.*.*-$CI_COMMIT_SHORT_SHA.bin expire_in: 1 week core fw regular production build: @@ -60,17 +60,17 @@ core fw regular production build: variables: PRODUCTION: "1" script: - - nix-shell --run "poetry run make -C core build_boardloader" - - nix-shell --run "poetry run make -C core build_bootloader" - - nix-shell --run "poetry run make -C core build_bootloader_ci" - - nix-shell --run "poetry run make -C core build_prodtest" - - nix-shell --run "poetry run make -C core build_firmware" - - nix-shell --run "poetry run make -C core sizecheck" - - cp core/build/firmware/firmware.bin trezor-fw-regular-production-$CORE_VERSION-$CI_COMMIT_SHORT_SHA.bin + - $NIX_SHELL --run "poetry run make -C core build_boardloader" + - $NIX_SHELL --run "poetry run make -C core build_bootloader" + - $NIX_SHELL --run "poetry run make -C core build_bootloader_ci" + - $NIX_SHELL --run "poetry run make -C core build_prodtest" + - $NIX_SHELL --run "poetry run make -C core build_firmware" + - $NIX_SHELL --run "poetry run make -C core sizecheck" + - cp core/build/firmware/firmware.bin firmware-T2T1-production-$CORE_VERSION-$CI_COMMIT_SHORT_SHA.bin artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: - - trezor-fw-regular-production-*.*.*-$CI_COMMIT_SHORT_SHA.bin + - firmware-T2T1-production-*.*.*-$CI_COMMIT_SHORT_SHA.bin expire_in: 1 week # Build of Core into firmware. Bitcoin-only version. @@ -81,14 +81,14 @@ core fw btconly build: variables: BITCOIN_ONLY: "1" script: - - nix-shell --run "poetry run make -C core build_firmware" + - $NIX_SHELL --run "poetry run make -C core build_firmware" - mv core/build/firmware/firmware.bin core/build/firmware/firmware-bitcoinonly.bin - - nix-shell --run "poetry run ./tools/check-bitcoin-only core/build/firmware/firmware-bitcoinonly.bin" - - cp core/build/firmware/firmware-bitcoinonly.bin trezor-fw-btconly-$CORE_VERSION-$CI_COMMIT_SHORT_SHA.bin + - $NIX_SHELL --run "poetry run ./tools/check-bitcoin-only core/build/firmware/firmware-bitcoinonly.bin" + - cp core/build/firmware/firmware-bitcoinonly.bin firmware-T2T1-btconly-$CORE_VERSION-$CI_COMMIT_SHORT_SHA.bin artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: - - trezor-fw-btconly-*.*.*-$CI_COMMIT_SHORT_SHA.bin + - firmware-T2T1-btconly-*.*.*-$CI_COMMIT_SHORT_SHA.bin expire_in: 1 week core fw btconly debug build: @@ -99,8 +99,8 @@ core fw btconly debug build: BITCOIN_ONLY: "1" PYOPT: "0" script: - - nix-shell --run "poetry run make -C core build_firmware" - - cp core/build/firmware/firmware.bin trezor-fw-btconly-debug-$CORE_VERSION-$CI_COMMIT_SHORT_SHA.bin + - $NIX_SHELL --run "poetry run make -C core build_firmware" + - cp core/build/firmware/firmware.bin firmware-T2T1-btconly-debug-$CORE_VERSION-$CI_COMMIT_SHORT_SHA.bin only: # currently used only for HW tests and deploys - schedules # nightly build - /^legacy\// @@ -111,7 +111,7 @@ core fw btconly debug build: artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: - - trezor-fw-btconly-*.*.*-$CI_COMMIT_SHORT_SHA.bin + - firmware-T2T1-btconly-*.*.*-$CI_COMMIT_SHORT_SHA.bin expire_in: 1 week core fw btconly production build: @@ -124,29 +124,13 @@ core fw btconly production build: PRODUCTION: "1" BITCOIN_ONLY: "1" script: - - nix-shell --run "poetry run make -C core build_firmware" - - nix-shell --run "poetry run ./tools/check-bitcoin-only core/build/firmware/firmware.bin" - - cp core/build/firmware/firmware.bin trezor-fw-btconly-production-$CORE_VERSION-$CI_COMMIT_SHORT_SHA.bin + - $NIX_SHELL --run "poetry run make -C core build_firmware" + - $NIX_SHELL --run "poetry run ./tools/check-bitcoin-only core/build/firmware/firmware.bin" + - cp core/build/firmware/firmware.bin firmware-T2T1-btconly-production-$CORE_VERSION-$CI_COMMIT_SHORT_SHA.bin artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: - - trezor-fw-btconly-production-*.*.*-$CI_COMMIT_SHORT_SHA.bin - expire_in: 1 week - -core fw btconly t1 build: - stage: build - <<: *gitlab_caching - needs: [] - variables: - BITCOIN_ONLY: "1" - TREZOR_MODEL: "1" - script: - - nix-shell --run "poetry run make -C core build_firmware" - - cp core/build/firmware/firmware.bin trezor-fw-btconly-t1-$CORE_VERSION-$CI_COMMIT_SHORT_SHA.bin - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - trezor-fw-btconly-t1-*.*.*-$CI_COMMIT_SHORT_SHA.bin + - firmware-T2T1-btconly-production-*.*.*-$CI_COMMIT_SHORT_SHA.bin expire_in: 1 week # Non-frozen emulator build. This means you still need Python files @@ -156,7 +140,7 @@ core unix regular build: <<: *gitlab_caching needs: [] script: - - nix-shell --run "poetry run make -C core build_unix" + - $NIX_SHELL --run "poetry run make -C core build_unix" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -172,7 +156,7 @@ core unix regular asan build: variables: ADDRESS_SANITIZER: "1" script: - - nix-shell --run "poetry run make -C core build_unix" + - $NIX_SHELL --run "poetry run make -C core build_unix" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -187,7 +171,7 @@ core unix frozen regular build: <<: *gitlab_caching needs: [] script: - - nix-shell --run "poetry run make -C core build_unix_frozen" + - $NIX_SHELL --run "poetry run make -C core build_unix_frozen" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -207,7 +191,7 @@ core unix frozen btconly debug build: PYOPT: "0" BITCOIN_ONLY: "1" script: - - nix-shell --run "poetry run make -C core build_unix_frozen" + - $NIX_SHELL --run "poetry run make -C core build_unix_frozen" - mv core/build/unix/trezor-emu-core core/build/unix/trezor-emu-core-bitcoinonly artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" @@ -226,7 +210,7 @@ core unix frozen btconly debug asan build: BITCOIN_ONLY: "1" ADDRESS_SANITIZER: "1" script: - - nix-shell --run "poetry run make -C core build_unix_frozen" + - $NIX_SHELL --run "poetry run make -C core build_unix_frozen" - mv core/build/unix/trezor-emu-core core/build/unix/trezor-emu-core-bitcoinonly artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" @@ -245,7 +229,7 @@ core unix frozen debug build: variables: PYOPT: "0" script: - - nix-shell --run "poetry run make -C core build_unix_frozen" + - $NIX_SHELL --run "poetry run make -C core build_unix_frozen" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" untracked: true @@ -261,7 +245,7 @@ core unix frozen debug asan build: PYOPT: "0" ADDRESS_SANITIZER: "1" script: - - nix-shell --run "poetry run make -C core build_unix_frozen" + - $NIX_SHELL --run "poetry run make -C core build_unix_frozen" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" untracked: true @@ -280,7 +264,7 @@ core unix frozen debug build arm: variables: PYOPT: "0" script: - - nix-shell --run "poetry run make -C core build_unix_frozen" + - $NIX_SHELL --run "poetry run make -C core build_unix_frozen" - mv core/build/unix/trezor-emu-core core/build/unix/trezor-emu-core-arm artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" @@ -289,22 +273,6 @@ core unix frozen debug build arm: tags: - docker_darwin_arm -core unix frozen btconly debug t1 build: - stage: build - <<: *gitlab_caching - needs: [] - variables: - BITCOIN_ONLY: "1" - TREZOR_MODEL: "1" - script: - - nix-shell --run "poetry run make -C core build_unix_frozen" - - mv core/build/unix/trezor-emu-core core/build/unix/trezor-emu-core-bitcoinonly - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - core/build/unix # most of it needed by test_rust - expire_in: 1 week - core macos frozen regular build: stage: build <<: *gitlab_caching @@ -313,7 +281,7 @@ core macos frozen regular build: tags: - darwin_arm script: - - nix-shell --option system x86_64-darwin --run "poetry run make -C core build_unix_frozen" + - $NIX_SHELL --option system x86_64-darwin --run "poetry run make -C core build_unix_frozen" - export NAME="trezor-emu-core.darwin" - cp -v core/build/unix/trezor-emu-core ./$NAME - chmod +x $NAME @@ -343,8 +311,8 @@ crypto build: - crypto/** script: - cp -r crypto crypto_noasan - - nix-shell --run "poetry run make -C crypto" - - nix-shell --run "export ADDRESS_SANITIZER=0; poetry run make -C crypto_noasan" + - $NIX_SHELL --run "poetry run make -C crypto" + - $NIX_SHELL --run "export ADDRESS_SANITIZER=0; poetry run make -C crypto_noasan" - mv crypto_noasan/tests/test_check crypto/tests/test_check_noasan artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" @@ -364,15 +332,15 @@ legacy fw regular build: <<: *gitlab_caching needs: [] script: - - nix-shell --run "export PRODUCTION=1 && poetry run legacy/script/cibuild" - - nix-shell --run "poetry run legacy/script/setup" - - nix-shell --run "export PRODUCTION=0 && poetry run legacy/script/cibuild" - - nix-shell --run "poetry run make -C legacy/demo" - - mv legacy/firmware/trezor.bin trezor-fw-regular-$LEGACY_VERSION-$CI_COMMIT_SHORT_SHA.bin + - $NIX_SHELL --run "export PRODUCTION=1 && poetry run legacy/script/cibuild" + - $NIX_SHELL --run "poetry run legacy/script/setup" + - $NIX_SHELL --run "export PRODUCTION=0 && poetry run legacy/script/cibuild" + - $NIX_SHELL --run "poetry run make -C legacy/demo" + - mv legacy/firmware/trezor.bin firmware-T1B1-$LEGACY_VERSION-$CI_COMMIT_SHORT_SHA.bin artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: - - trezor-fw-regular-*.*.*-$CI_COMMIT_SHORT_SHA.bin + - firmware-T1B1-*.*.*-$CI_COMMIT_SHORT_SHA.bin expire_in: 1 week legacy fw regular debug build: @@ -382,14 +350,14 @@ legacy fw regular debug build: variables: DEBUG_LINK: "1" script: - - nix-shell --run "export PRODUCTION=1 && poetry run legacy/script/cibuild" - - nix-shell --run "poetry run legacy/script/setup" - - nix-shell --run "export PRODUCTION=0 && poetry run legacy/script/cibuild" - - mv legacy/firmware/trezor.bin trezor-fw-regular-debug-$LEGACY_VERSION-$CI_COMMIT_SHORT_SHA.bin + - $NIX_SHELL --run "export PRODUCTION=1 && poetry run legacy/script/cibuild" + - $NIX_SHELL --run "poetry run legacy/script/setup" + - $NIX_SHELL --run "export PRODUCTION=0 && poetry run legacy/script/cibuild" + - mv legacy/firmware/trezor.bin firmware-T1B1-debug-$LEGACY_VERSION-$CI_COMMIT_SHORT_SHA.bin artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: - - trezor-fw-regular-debug-*.*.*-$CI_COMMIT_SHORT_SHA.bin + - firmware-T1B1-debug-*.*.*-$CI_COMMIT_SHORT_SHA.bin expire_in: 1 week legacy fw btconly build: @@ -399,16 +367,16 @@ legacy fw btconly build: variables: BITCOIN_ONLY: "1" script: - - nix-shell --run "export PRODUCTION=1 && poetry run legacy/script/cibuild" - - nix-shell --run "poetry run legacy/script/setup" - - nix-shell --run "export PRODUCTION=0 && poetry run legacy/script/cibuild" + - $NIX_SHELL --run "export PRODUCTION=1 && poetry run legacy/script/cibuild" + - $NIX_SHELL --run "poetry run legacy/script/setup" + - $NIX_SHELL --run "export PRODUCTION=0 && poetry run legacy/script/cibuild" - mv legacy/firmware/trezor.bin legacy/firmware/trezor-bitcoinonly.bin - - nix-shell --run "poetry run ./tools/check-bitcoin-only legacy/firmware/trezor-bitcoinonly.bin" - - mv legacy/firmware/trezor-bitcoinonly.bin trezor-fw-btconly-$LEGACY_VERSION-$CI_COMMIT_SHORT_SHA.bin + - $NIX_SHELL --run "poetry run ./tools/check-bitcoin-only legacy/firmware/trezor-bitcoinonly.bin" + - mv legacy/firmware/trezor-bitcoinonly.bin firmware-T1B1-btconly-$LEGACY_VERSION-$CI_COMMIT_SHORT_SHA.bin artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: - - trezor-fw-btconly-*.*.*-$CI_COMMIT_SHORT_SHA.bin + - firmware-T1B1-btconly-*.*.*-$CI_COMMIT_SHORT_SHA.bin expire_in: 1 week legacy fw btconly debug build: @@ -419,15 +387,15 @@ legacy fw btconly debug build: BITCOIN_ONLY: "1" DEBUG_LINK: "1" script: - - nix-shell --run "export PRODUCTION=1 && poetry run legacy/script/cibuild" - - nix-shell --run "poetry run legacy/script/setup" - - nix-shell --run "export PRODUCTION=0 && poetry run legacy/script/cibuild" - - nix-shell --run "poetry run ./tools/check-bitcoin-only legacy/firmware/trezor.bin" - - mv legacy/firmware/trezor.bin trezor-fw-btconly-debug-$LEGACY_VERSION-$CI_COMMIT_SHORT_SHA.bin + - $NIX_SHELL --run "export PRODUCTION=1 && poetry run legacy/script/cibuild" + - $NIX_SHELL --run "poetry run legacy/script/setup" + - $NIX_SHELL --run "export PRODUCTION=0 && poetry run legacy/script/cibuild" + - $NIX_SHELL --run "poetry run ./tools/check-bitcoin-only legacy/firmware/trezor.bin" + - mv legacy/firmware/trezor.bin firmware-T1B1-btconly-debug-$LEGACY_VERSION-$CI_COMMIT_SHORT_SHA.bin artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: - - trezor-fw-btconly-*.*.*-$CI_COMMIT_SHORT_SHA.bin + - firmware-T1B1-btconly-debug-*.*.*-$CI_COMMIT_SHORT_SHA.bin expire_in: 1 week # Regular version (not only Bitcoin) of above. @@ -440,7 +408,7 @@ legacy emu regular debug build: DEBUG_LINK: "1" EMULATOR: "1" script: - - nix-shell --run "poetry run legacy/script/cibuild" + - $NIX_SHELL --run "poetry run legacy/script/cibuild" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -458,7 +426,7 @@ legacy emu regular debug asan build: EMULATOR: "1" ADDRESS_SANITIZER: "1" script: - - nix-shell --run "poetry run legacy/script/cibuild" + - $NIX_SHELL --run "poetry run legacy/script/cibuild" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -479,7 +447,7 @@ legacy emu regular debug build arm: DEBUG_LINK: "1" EMULATOR: "1" script: - - nix-shell --run "poetry run legacy/script/cibuild" + - $NIX_SHELL --run "poetry run legacy/script/cibuild" - mv legacy/firmware/trezor.elf legacy/firmware/trezor-arm.elf artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" @@ -500,7 +468,7 @@ legacy emu btconly debug build: DEBUG_LINK: "1" EMULATOR: "1" script: - - nix-shell --run "poetry run legacy/script/cibuild" + - $NIX_SHELL --run "poetry run legacy/script/cibuild" - mv legacy/firmware/trezor.elf legacy/firmware/trezor-bitcoinonly.elf artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" @@ -520,7 +488,7 @@ legacy emu btconly debug asan build: EMULATOR: "1" ADDRESS_SANITIZER: "1" script: - - nix-shell --run "poetry run legacy/script/cibuild" + - $NIX_SHELL --run "poetry run legacy/script/cibuild" - mv legacy/firmware/trezor.elf legacy/firmware/trezor-bitcoinonly.elf artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" diff --git a/ci/deploy.yml b/ci/deploy.yml index b34e76f267..8b141f0d72 100644 --- a/ci/deploy.yml +++ b/ci/deploy.yml @@ -12,7 +12,7 @@ release core fw regular deploy: - core fw regular build script: - export VERSION=$(./tools/version.sh core/embed/firmware/version.h) - - export NAME="trezor-fw-regular-$VERSION-$CI_COMMIT_SHORT_SHA.bin" + - export NAME="firmware-T2T1-$VERSION-$CI_COMMIT_SHORT_SHA.bin" - echo "Deploying to ${DEPLOY_DIRECTORY}/$NAME" - mkdir -p "${DEPLOY_BASE_DIR}/${DEPLOY_DIRECTORY}" - rsync --delete -va $NAME "${DEPLOY_BASE_DIR}/${DEPLOY_DIRECTORY}/$NAME" @@ -33,7 +33,7 @@ release core fw btconly deploy: - core fw btconly build script: - export VERSION=$(./tools/version.sh core/embed/firmware/version.h) - - export NAME="trezor-fw-btconly-$VERSION-$CI_COMMIT_SHORT_SHA.bin" + - export NAME="firmware-T2T1-btconly-$VERSION-$CI_COMMIT_SHORT_SHA.bin" - echo "Deploying to ${DEPLOY_DIRECTORY}/$NAME" - mkdir -p "${DEPLOY_BASE_DIR}/${DEPLOY_DIRECTORY}" - rsync --delete -va $NAME "${DEPLOY_BASE_DIR}/${DEPLOY_DIRECTORY}/$NAME" @@ -54,7 +54,7 @@ release core fw regular debug deploy: - core fw regular debug build script: - export VERSION=$(./tools/version.sh core/embed/firmware/version.h) - - export NAME="trezor-fw-regular-debug-$VERSION-$CI_COMMIT_SHORT_SHA.bin" + - export NAME="firmware-T2T1-debug-$VERSION-$CI_COMMIT_SHORT_SHA.bin" - echo "Deploying to ${DEPLOY_DIRECTORY}/$NAME" - mkdir -p "${DEPLOY_BASE_DIR}/${DEPLOY_DIRECTORY}" - rsync --delete -va $NAME "${DEPLOY_BASE_DIR}/${DEPLOY_DIRECTORY}/$NAME" @@ -75,7 +75,7 @@ release core fw btconly debug deploy: - core fw btconly debug build script: - export VERSION=$(./tools/version.sh core/embed/firmware/version.h) - - export NAME="trezor-fw-btconly-debug-$VERSION-$CI_COMMIT_SHORT_SHA.bin" + - export NAME="firmware-T2T1-btconly-debug-$VERSION-$CI_COMMIT_SHORT_SHA.bin" - echo "Deploying to ${DEPLOY_DIRECTORY}/$NAME" - mkdir -p "${DEPLOY_BASE_DIR}/${DEPLOY_DIRECTORY}" - rsync --delete -va $NAME "${DEPLOY_BASE_DIR}/${DEPLOY_DIRECTORY}/$NAME" @@ -98,7 +98,7 @@ release legacy fw regular deploy: - legacy fw regular build script: - export VERSION=$(./tools/version.sh legacy/firmware/version.h) - - export NAME="trezor-fw-regular-$VERSION-$CI_COMMIT_SHORT_SHA.bin" + - export NAME="firmware-T1B1-$VERSION-$CI_COMMIT_SHORT_SHA.bin" - echo "Deploying to ${DEPLOY_DIRECTORY}/$NAME" - mkdir -p "${DEPLOY_BASE_DIR}/${DEPLOY_DIRECTORY}" - rsync --delete -va $NAME "${DEPLOY_BASE_DIR}/${DEPLOY_DIRECTORY}/$NAME" @@ -119,7 +119,7 @@ release legacy fw btconly deploy: - legacy fw btconly build script: - export VERSION=$(./tools/version.sh legacy/firmware/version.h) - - export NAME="trezor-fw-btconly-$VERSION-$CI_COMMIT_SHORT_SHA.bin" + - export NAME="firmware-T1B1-btconly-$VERSION-$CI_COMMIT_SHORT_SHA.bin" - echo "Deploying to ${DEPLOY_DIRECTORY}/$NAME" - mkdir -p "${DEPLOY_BASE_DIR}/${DEPLOY_DIRECTORY}" - rsync --delete -va $NAME "${DEPLOY_BASE_DIR}/${DEPLOY_DIRECTORY}/$NAME" @@ -140,7 +140,7 @@ release legacy fw regular debug deploy: - legacy fw regular debug build script: - export VERSION=$(./tools/version.sh legacy/firmware/version.h) - - export NAME="trezor-fw-regular-debug-$VERSION-$CI_COMMIT_SHORT_SHA.bin" + - export NAME="firmware-T1B1-debug-$VERSION-$CI_COMMIT_SHORT_SHA.bin" - echo "Deploying to ${DEPLOY_DIRECTORY}/$NAME" - mkdir -p "${DEPLOY_BASE_DIR}/${DEPLOY_DIRECTORY}" - rsync --delete -va $NAME "${DEPLOY_BASE_DIR}/${DEPLOY_DIRECTORY}/$NAME" @@ -161,7 +161,7 @@ release legacy fw btconly debug deploy: - legacy fw btconly debug build script: - export VERSION=$(./tools/version.sh legacy/firmware/version.h) - - export NAME="trezor-fw-btconly-debug-$VERSION-$CI_COMMIT_SHORT_SHA.bin" + - export NAME="firmware-T1B1-btconly-debug-$VERSION-$CI_COMMIT_SHORT_SHA.bin" - echo "Deploying to ${DEPLOY_DIRECTORY}/$NAME" - mkdir -p "${DEPLOY_BASE_DIR}/${DEPLOY_DIRECTORY}" - rsync --delete -va $NAME "${DEPLOY_BASE_DIR}/${DEPLOY_DIRECTORY}/$NAME" @@ -188,8 +188,8 @@ release core unix debug deploy: - DEST="$DEPLOY_PATH/trezor-emu-core-v$VERSION" - DEST_ARM="$DEPLOY_PATH/arm/trezor-emu-core-v$VERSION-arm" - echo "Deploying to $DEST and $DEST_ARM" - - nix-shell -p patchelf --run "patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2 core/build/unix/trezor-emu-core" - - nix-shell -p patchelf --run "patchelf --set-interpreter /lib/ld-linux-aarch64.so.1 core/build/unix/trezor-emu-core-arm" + - $NIX_SHELL -p patchelf --run "patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2 core/build/unix/trezor-emu-core" + - $NIX_SHELL -p patchelf --run "patchelf --set-interpreter /lib/ld-linux-aarch64.so.1 core/build/unix/trezor-emu-core-arm" - rsync --delete -va core/build/unix/trezor-emu-core "$DEST" - rsync --delete -va core/build/unix/trezor-emu-core-arm "$DEST_ARM" only: @@ -213,8 +213,8 @@ release legacy unix debug deploy: - DEST="$DEPLOY_PATH/trezor-emu-legacy-v$VERSION" - DEST_ARM="$DEPLOY_PATH/arm/trezor-emu-legacy-v$VERSION-arm" - echo "Deploying to $DEST and $DEST_ARM" - - nix-shell -p patchelf --run "patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2 legacy/firmware/trezor.elf" - - nix-shell -p patchelf --run "patchelf --set-interpreter /lib/ld-linux-aarch64.so.1 legacy/firmware/trezor-arm.elf" + - $NIX_SHELL -p patchelf --run "patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2 legacy/firmware/trezor.elf" + - $NIX_SHELL -p patchelf --run "patchelf --set-interpreter /lib/ld-linux-aarch64.so.1 legacy/firmware/trezor-arm.elf" - rsync --delete -va legacy/firmware/trezor.elf "$DEST" - rsync --delete -va legacy/firmware/trezor-arm.elf "$DEST_ARM" only: @@ -234,6 +234,7 @@ ui tests fixtures deploy: GIT_SUBMODULE_STRATEGY: "none" before_script: [] # no poetry needs: + - core click test - core device test - legacy device test script: diff --git a/ci/hardware_tests/t1_hw_test.sh b/ci/hardware_tests/t1_hw_test.sh index 2205059ac9..66eeafe8be 100755 --- a/ci/hardware_tests/t1_hw_test.sh +++ b/ci/hardware_tests/t1_hw_test.sh @@ -17,5 +17,5 @@ set +a ./record_video.sh ${T1_CAMERA} ${CI_COMMIT_SHORT_SHA} start (cd ../.. && poetry install) poetry run python bootstrap.py t1 -poetry run python bootstrap.py t1 ../../trezor-*.bin +poetry run python bootstrap.py t1 ../../firmware-T1*.bin poetry run pytest ../../tests/device_tests diff --git a/ci/posttest.yml b/ci/posttest.yml index afde3fe59e..3524f2a22b 100644 --- a/ci/posttest.yml +++ b/ci/posttest.yml @@ -13,12 +13,13 @@ core unix coverage posttest: COVERAGE_THRESHOLD: "78" <<: *gitlab_caching needs: + - core click test - core device test - core monero test - core u2f test - core fido2 test script: - - nix-shell --run "poetry run make -C core coverage" + - $NIX_SHELL --run "poetry run make -C core coverage" coverage: '/COVERAGE: \d+%/' artifacts: name: core-unix-coverage-posttest @@ -34,11 +35,12 @@ unix ui changes: - master <<: *gitlab_caching needs: + - core click test - core device test - legacy device test script: - - nix-shell --run "cd tests/ui_tests ; poetry run python reporting/report_master_diff.py TT_ T1_" - - mv tests/ui_tests/reporting/reports/master_diff/ . + - $NIX_SHELL --run "poetry run python -m tests.ui_tests.reporting master-diff" + - mv tests/ui_tests/reports/master_diff . artifacts: name: core-unix-ui-changes paths: diff --git a/ci/prebuild.yml b/ci/prebuild.yml index e97215402d..7c4057c915 100644 --- a/ci/prebuild.yml +++ b/ci/prebuild.yml @@ -19,35 +19,28 @@ style prebuild: GIT_SUBMODULE_STRATEGY: "none" <<: *gitlab_caching script: - - nix-shell --run "poetry run make -j1 style_check" + - $NIX_SHELL --run "poetry run make -j1 style_check" # Check validity of coin definitions and protobuf files. common prebuild: stage: prebuild <<: *gitlab_caching script: - - nix-shell --run "poetry run make defs_check" + - $NIX_SHELL --run "poetry run make defs_check" # Check validity of auto-generated files. gen prebuild: stage: prebuild <<: *gitlab_caching script: - - nix-shell --run "poetry run make -j1 gen_check" + - $NIX_SHELL --run "poetry run make -j1 gen_check" # Checking format of .editorconfig files. editor prebuild: stage: prebuild <<: *gitlab_caching script: - - nix-shell --run "make editor_check" - -# All .yml/.yaml files are checked for syntax validity and other correctness. -yaml prebuild: - stage: prebuild - <<: *gitlab_caching - script: - - nix-shell --run "poetry run make yaml_check" + - $NIX_SHELL --run "make editor_check" # Checking the format of release commit messages. release commit messages prebuild: @@ -63,7 +56,7 @@ release commit messages prebuild: # We want this to run on gitlab.com/satoshilabs/trezor/trezor-firmware only. - $CI_PROJECT_PATH_SLUG == 'satoshilabs-trezor-trezor-firmware' script: - - nix-shell --run "ci/check_release_commit_messages.sh" + - $NIX_SHELL --run "ci/check_release_commit_messages.sh" # Verifying that all commits changing some functionality have a changelog entry # or contain `[no changelog]` in the commit message. @@ -76,4 +69,4 @@ changelog prebuild: GIT_SUBMODULE_STRATEGY: "none" GIT_STRATEGY: clone script: - - nix-shell --run "ci/check_changelog.sh" + - $NIX_SHELL --run "ci/check_changelog.sh" diff --git a/ci/prepare_ui_artifacts.py b/ci/prepare_ui_artifacts.py index 0efa556a29..f5fc8f753c 100644 --- a/ci/prepare_ui_artifacts.py +++ b/ci/prepare_ui_artifacts.py @@ -5,24 +5,24 @@ from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT)) # Needed for setup purposes, filling the FILE_HASHES dict -from tests.ui_tests import read_fixtures # isort:skip +from tests.ui_tests.common import TestResult, _hash_files, get_fixtures # isort:skip -read_fixtures() -from tests.ui_tests import _hash_files, FILE_HASHES, SCREENS_DIR # isort:skip -# As in CI we are running T1 and TT tests separately, there will -# always be the other model missing. -# Therefore, choosing just the cases for our model. -if len(sys.argv) > 1 and sys.argv[1].upper() == "T1": - model = "T1" -else: - model = "TT" -model_file_hashes = {k: v for k, v in FILE_HASHES.items() if k.startswith(f"{model}_")} +FIXTURES = get_fixtures() -for test_case, expected_hash in model_file_hashes.items(): - recorded_dir = SCREENS_DIR / test_case / "recorded" - actual_hash = _hash_files(recorded_dir) +for result in TestResult.recent_results(): + if not result.passed or result.expected_hash != result.actual_hash: + print("WARNING: skipping failed test", result.test.id) + continue + + actual_hash = _hash_files(result.test.actual_dir) + expected_hash = ( + FIXTURES.get(result.test.model, {}) + .get(result.test.group, {}) + .get(result.test.fixtures_name) + ) + assert result.expected_hash == actual_hash assert expected_hash == actual_hash shutil.make_archive( - str(ROOT / "ci/ui_test_records" / actual_hash), "zip", recorded_dir + str(ROOT / "ci/ui_test_records" / actual_hash), "zip", result.test.actual_dir ) diff --git a/ci/test-hw.yml b/ci/test-hw.yml index d4d01ef4a8..b909192b42 100644 --- a/ci/test-hw.yml +++ b/ci/test-hw.yml @@ -41,9 +41,9 @@ hardware core regular device test: - set -a - source hardware.cfg - set +a - - nix-shell --run "cd ../.. && poetry install" - - nix-shell --run "poetry run python bootstrap.py tt ../../trezor-*.bin | ts -s" - - nix-shell --run "poetry run pytest ../../tests/device_tests | ts -s" + - $NIX_SHELL --run "cd ../.. && poetry install" + - $NIX_SHELL --run "poetry run python bootstrap.py tt ../../firmware-T2*.bin | ts -s" + - $NIX_SHELL --run "poetry run pytest ../../tests/device_tests | ts -s" timeout: 6h artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" @@ -71,9 +71,9 @@ hardware core btconly device test: - set -a - source hardware.cfg - set +a - - nix-shell --run "cd ../.. && poetry install" - - nix-shell --run "poetry run python bootstrap.py tt ../../trezor-*.bin | ts -s" - - nix-shell --run "poetry run pytest ../../tests/device_tests | ts -s" + - $NIX_SHELL --run "cd ../.. && poetry install" + - $NIX_SHELL --run "poetry run python bootstrap.py tt ../../firmware-T2*.bin | ts -s" + - $NIX_SHELL --run "poetry run pytest ../../tests/device_tests | ts -s" timeout: 4h artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" @@ -98,9 +98,9 @@ hardware core monero test: - set -a - source hardware.cfg - set +a - - nix-shell --run "cd ../.. && poetry install" - - nix-shell --run "poetry run python bootstrap.py tt ../../trezor-*.bin | ts -s" - - nix-shell --arg fullDeps true --run "cd ../../core/tests && ./run_tests_device_emu_monero.sh $TESTOPTS | ts -s" + - $NIX_SHELL --run "cd ../.. && poetry install" + - $NIX_SHELL --run "poetry run python bootstrap.py tt ../../firmware-T2*.bin | ts -s" + - $NIX_SHELL --arg fullDeps true --run "cd ../../core/tests && ./run_tests_device_emu_monero.sh $TESTOPTS | ts -s" timeout: 1h artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" @@ -124,7 +124,7 @@ hardware legacy regular device test: - legacy fw regular debug build script: - cd ci/hardware_tests - - nix-shell --run "./t1_hw_test.sh | ts -s" + - $NIX_SHELL --run "./t1_hw_test.sh | ts -s" timeout: 1h10m artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" @@ -150,7 +150,7 @@ hardware legacy btconly device test: - legacy fw btconly debug build script: - cd ci/hardware_tests - - nix-shell --run "./t1_hw_test.sh | ts -s" + - $NIX_SHELL --run "./t1_hw_test.sh | ts -s" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: diff --git a/ci/test.yml b/ci/test.yml index 37cdc8dc67..b72f6cbbe5 100644 --- a/ci/test.yml +++ b/ci/test.yml @@ -18,7 +18,7 @@ core unit python test: needs: - core unix regular build script: - - nix-shell --run "poetry run make -C core test | ts -s" + - $NIX_SHELL --run "poetry run make -C core test | ts -s" # Rust unit tests. core unit rust test: @@ -27,8 +27,8 @@ core unit rust test: needs: - core unix frozen debug build script: - - nix-shell --run "poetry run make -C core clippy | ts -s" - - nix-shell --run "poetry run make -C core test_rust | ts -s" + - $NIX_SHELL --run "poetry run make -C core clippy | ts -s" + - $NIX_SHELL --run "poetry run make -C core test_rust | ts -s" core unit asan test: stage: test @@ -43,20 +43,9 @@ core unit asan test: ADDRESS_SANITIZER: "1" LSAN_OPTIONS: "suppressions=../../asan_suppressions.txt" script: - - nix-shell --run "poetry run make -C core test | ts -s" - - nix-shell --run "poetry run make -C core clean build_unix | ts -s" - - nix-shell --run "poetry run make -C core test_rust | ts -s" - -core unit t1 test: - stage: test - <<: *gitlab_caching - needs: - - core unix frozen btconly debug t1 build - variables: - BITCOIN_ONLY: "1" - TREZOR_MODEL: "1" - script: - - nix-shell --run "poetry run make -C core test_rust | ts -s" + - $NIX_SHELL --run "poetry run make -C core test | ts -s" + - $NIX_SHELL --run "poetry run make -C core clean build_unix | ts -s" + - $NIX_SHELL --run "poetry run make -C core test_rust | ts -s" # Device tests for Core. Running device tests and also comparing screens # with the expected UI result. @@ -70,12 +59,12 @@ core device test: variables: TREZOR_PROFILING: 1 script: - - nix-shell --run "poetry run make -C core test_emu_ui | ts -s" - - mv core/src/.coverage core/.coverage.test_emu + - $NIX_SHELL --run "poetry run make -C core test_emu_ui | ts -s" after_script: - - mv tests/ui_tests/reporting/reports/test/ test_ui_report - - nix-shell --run "poetry run python ci/prepare_ui_artifacts.py TT | ts -s" - - diff tests/ui_tests/fixtures.json tests/ui_tests/fixtures.suggestion.json + - mv core/src/.coverage core/.coverage.test_emu + - mv tests/ui_tests/reports/test/ test_ui_report + - $NIX_SHELL --run "poetry run python ci/prepare_ui_artifacts.py | ts -s" + - diff -u tests/ui_tests/fixtures.json tests/ui_tests/fixtures.suggestion.json artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -101,7 +90,7 @@ core device asan test: variables: PYTEST_TIMEOUT: "600" script: - - nix-shell --run "poetry run make -C core test_emu | ts -s" + - $NIX_SHELL --run "poetry run make -C core test_emu | ts -s" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -119,7 +108,7 @@ core btconly device test: MICROPYTHON: "build/unix/trezor-emu-core-bitcoinonly" TREZOR_PYTEST_SKIP_ALTCOINS: 1 script: - - nix-shell --run "poetry run make -C core test_emu | ts -s" + - $NIX_SHELL --run "poetry run make -C core test_emu | ts -s" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -142,7 +131,7 @@ core btconly device asan test: TREZOR_PYTEST_SKIP_ALTCOINS: 1 PYTEST_TIMEOUT: "600" script: - - nix-shell --run "poetry run make -C core test_emu | ts -s" + - $NIX_SHELL --run "poetry run make -C core test_emu | ts -s" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -159,7 +148,7 @@ core monero test: variables: TREZOR_PROFILING: 1 script: - - nix-shell --arg fullDeps true --run "poetry run make -C core test_emu_monero | ts -s" + - $NIX_SHELL --arg fullDeps true --run "poetry run make -C core test_emu_monero | ts -s" - mv core/src/.coverage core/.coverage.test_emu_monero artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" @@ -180,7 +169,7 @@ core monero asan test: variables: TREZOR_PROFILING: 1 script: - - nix-shell --arg fullDeps true --run "poetry run make -C core test_emu_monero | ts -s" + - $NIX_SHELL --arg fullDeps true --run "poetry run make -C core test_emu_monero | ts -s" - mv core/src/.coverage core/.coverage.test_emu_monero artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" @@ -200,8 +189,8 @@ core u2f test: variables: TREZOR_PROFILING: 1 script: - - nix-shell --run "poetry run make -C tests/fido_tests/u2f-tests-hid | ts -s" - - nix-shell --run "poetry run make -C core test_emu_u2f | ts -s" + - $NIX_SHELL --run "poetry run make -C tests/fido_tests/u2f-tests-hid | ts -s" + - $NIX_SHELL --run "poetry run make -C core test_emu_u2f | ts -s" - mv core/src/.coverage core/.coverage.test_emu_u2f artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" @@ -219,8 +208,8 @@ core u2f asan test: only: - schedules # nightly build script: - - nix-shell --run "poetry run make -C tests/fido_tests/u2f-tests-hid | ts -s" - - nix-shell --run "poetry run make -C core test_emu_u2f | ts -s" + - $NIX_SHELL --run "poetry run make -C tests/fido_tests/u2f-tests-hid | ts -s" + - $NIX_SHELL --run "poetry run make -C core test_emu_u2f | ts -s" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -238,7 +227,7 @@ core fido2 test: TREZOR_PROFILING: 1 script: - pgrep trezor-emu-core || true - - nix-shell --run "poetry run make -C core test_emu_fido2 | ts -s" + - $NIX_SHELL --run "poetry run make -C core test_emu_fido2 | ts -s" - pgrep trezor-emu-core || true - mv core/src/.coverage core/.coverage.test_emu_fido2 artifacts: @@ -261,7 +250,7 @@ core fido2 asan test: - schedules # nightly build script: - pgrep trezor-emu-core || true - - nix-shell --run "poetry run make -C core test_emu_fido2 | ts -s" + - $NIX_SHELL --run "poetry run make -C core test_emu_fido2 | ts -s" - pgrep trezor-emu-core || true artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" @@ -277,11 +266,22 @@ core click test: <<: *gitlab_caching needs: - core unix frozen debug build + variables: + TREZOR_PROFILING: 1 script: - - nix-shell --run "poetry run make -C core test_emu_click | ts -s" + - $NIX_SHELL --run "poetry run make -C core test_emu_click_ui | ts -s" + after_script: + - mv core/src/.coverage core/.coverage.test_click + - mv tests/ui_tests/reports/test/ test_ui_report + - $NIX_SHELL --run "poetry run python ci/prepare_ui_artifacts.py | ts -s" + - diff -u tests/ui_tests/fixtures.json tests/ui_tests/fixtures.suggestion.json artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: + - ci/ui_test_records/ + - test_ui_report + - tests/ui_tests/screens/ + - tests/ui_tests/fixtures.suggestion.json - tests/trezor.log - tests/junit.xml reports: @@ -297,7 +297,7 @@ core click asan test: only: - schedules # nightly build script: - - nix-shell --run "poetry run make -C core test_emu_click | ts -s" + - $NIX_SHELL --run "poetry run make -C core test_emu_click | ts -s" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -318,8 +318,8 @@ core upgrade test: variables: TREZOR_UPGRADE_TEST: "core" script: - - nix-shell --run "tests/download_emulators.sh" - - nix-shell --run "poetry run pytest --junitxml=tests/junit.xml tests/upgrade_tests | ts -s" + - $NIX_SHELL --run "tests/download_emulators.sh" + - $NIX_SHELL --run "poetry run pytest --junitxml=tests/junit.xml tests/upgrade_tests | ts -s" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -339,8 +339,8 @@ core upgrade asan test: variables: TREZOR_UPGRADE_TEST: "core" script: - - nix-shell --run "tests/download_emulators.sh" - - nix-shell --run "poetry run pytest --junitxml=tests/junit.xml tests/upgrade_tests | ts -s" + - $NIX_SHELL --run "tests/download_emulators.sh" + - $NIX_SHELL --run "poetry run pytest --junitxml=tests/junit.xml tests/upgrade_tests | ts -s" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -357,7 +357,7 @@ core persistence test: needs: - core unix frozen debug build script: - - nix-shell --run "poetry run pytest --junitxml=tests/junit.xml tests/persistence_tests | ts -s" + - $NIX_SHELL --run "poetry run pytest --junitxml=tests/junit.xml tests/persistence_tests | ts -s" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -375,7 +375,7 @@ core persistence asan test: only: - schedules # nightly build script: - - nix-shell --run "poetry run pytest --junitxml=tests/junit.xml tests/persistence_tests | ts -s" + - $NIX_SHELL --run "poetry run pytest --junitxml=tests/junit.xml tests/persistence_tests | ts -s" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -392,8 +392,8 @@ core hwi test: - core unix frozen debug build allow_failure: true script: - - nix-shell --run "git clone https://github.com/bitcoin-core/HWI.git" - - nix-shell --arg fullDeps true --run "cd HWI && poetry install && poetry run ./test/test_trezor.py --model_t ../core/build/unix/trezor-emu-core bitcoind" + - $NIX_SHELL --run "git clone https://github.com/bitcoin-core/HWI.git" + - $NIX_SHELL --arg fullDeps true --run "cd HWI && poetry install && poetry run ./test/test_trezor.py --model_t ../core/build/unix/trezor-emu-core bitcoind" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -420,8 +420,8 @@ crypto test: - ./crypto/tests/aestst - ./crypto/tests/test_check - ./crypto/tests/test_openssl 1000 - - nix-shell --run "cd crypto && ITERS=10 poetry run pytest --junitxml=tests/junit.xml tests | ts -s" - - nix-shell --run "CK_TIMEOUT_MULTIPLIER=20 valgrind -q --error-exitcode=1 ./crypto/tests/test_check_noasan | ts -s" + - $NIX_SHELL --run "cd crypto && ITERS=10 poetry run pytest --junitxml=tests/junit.xml tests | ts -s" + - $NIX_SHELL --run "CK_TIMEOUT_MULTIPLIER=20 valgrind -q --error-exitcode=1 ./crypto/tests/test_check_noasan | ts -s" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -442,11 +442,11 @@ legacy device test: variables: EMULATOR: "1" script: - - nix-shell --run "poetry run make -C legacy test_emu_ui | ts -s" + - $NIX_SHELL --run "poetry run make -C legacy test_emu_ui | ts -s" after_script: - - mv tests/ui_tests/reporting/reports/test/ test_ui_report - - nix-shell --run "poetry run python ci/prepare_ui_artifacts.py T1 | ts -s" - - diff tests/ui_tests/fixtures.json tests/ui_tests/fixtures.suggestion.json + - mv tests/ui_tests/reports/test/ test_ui_report + - $NIX_SHELL --run "poetry run python ci/prepare_ui_artifacts.py | ts -s" + - diff -u tests/ui_tests/fixtures.json tests/ui_tests/fixtures.suggestion.json artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -471,7 +471,7 @@ legacy asan test: variables: EMULATOR: "1" script: - - nix-shell --run "poetry run make -C legacy test_emu | ts -s" + - $NIX_SHELL --run "poetry run make -C legacy test_emu | ts -s" legacy btconly test: stage: test @@ -483,7 +483,7 @@ legacy btconly test: EMULATOR_BINARY: "firmware/trezor-bitcoinonly.elf" TREZOR_PYTEST_SKIP_ALTCOINS: 1 script: - - nix-shell --run "poetry run make -C legacy test_emu | ts -s" + - $NIX_SHELL --run "poetry run make -C legacy test_emu | ts -s" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -505,7 +505,7 @@ legacy btconly asan test: EMULATOR_BINARY: "firmware/trezor-bitcoinonly.elf" TREZOR_PYTEST_SKIP_ALTCOINS: 1 script: - - nix-shell --run "poetry run make -C legacy test_emu | ts -s" + - $NIX_SHELL --run "poetry run make -C legacy test_emu | ts -s" legacy upgrade test: @@ -516,8 +516,8 @@ legacy upgrade test: variables: TREZOR_UPGRADE_TEST: "legacy" script: - - nix-shell --run "tests/download_emulators.sh" - - nix-shell --run "poetry run pytest --junitxml=tests/junit.xml tests/upgrade_tests | ts -s" + - $NIX_SHELL --run "tests/download_emulators.sh" + - $NIX_SHELL --run "poetry run pytest --junitxml=tests/junit.xml tests/upgrade_tests | ts -s" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -537,8 +537,8 @@ legacy upgrade asan test: variables: TREZOR_UPGRADE_TEST: "legacy" script: - - nix-shell --run "tests/download_emulators.sh" - - nix-shell --run "poetry run pytest --junitxml=tests/junit.xml tests/upgrade_tests | ts -s" + - $NIX_SHELL --run "tests/download_emulators.sh" + - $NIX_SHELL --run "poetry run pytest --junitxml=tests/junit.xml tests/upgrade_tests | ts -s" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -557,8 +557,8 @@ legacy hwi test: EMULATOR: "1" allow_failure: true script: - - nix-shell --run "git clone https://github.com/bitcoin-core/HWI.git" - - nix-shell --arg fullDeps true --run "cd HWI && poetry install && poetry run ./test/test_trezor.py --model_1 ../legacy/firmware/trezor.elf bitcoind" + - $NIX_SHELL --run "git clone https://github.com/bitcoin-core/HWI.git" + - $NIX_SHELL --arg fullDeps true --run "cd HWI && poetry install && poetry run ./test/test_trezor.py --model_1 ../legacy/firmware/trezor.elf bitcoind" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -584,14 +584,14 @@ python test: # See also: # https://github.com/NixOS/nixpkgs/blob/b00c7c2d1d905eb63c81a0917f1a94b763a7843b/pkgs/development/interpreters/python/cpython/default.nix#L103 # https://github.com/NixOS/nixpkgs/pull/98915 - - nix-shell --arg fullDeps true --run "unset _PYTHON_SYSCONFIGDATA_NAME && cd python && poetry run tox | ts -s" + - $NIX_SHELL --arg fullDeps true --run "unset _PYTHON_SYSCONFIGDATA_NAME && cd python && poetry run tox | ts -s" python support test: stage: test <<: *gitlab_caching needs: [] script: - - nix-shell --run "poetry run make python_support_check | ts -s" + - $NIX_SHELL --run "poetry run make python_support_check | ts -s" # Storage @@ -607,8 +607,8 @@ storage test: needs: [] script: - unset PYTEST_TIMEOUT - - nix-shell --run "poetry run make -C storage/tests build | ts -s" - - nix-shell --run "poetry run make -C storage/tests tests_all | ts -s" + - $NIX_SHELL --run "poetry run make -C storage/tests build | ts -s" + - $NIX_SHELL --run "poetry run make -C storage/tests tests_all | ts -s" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -630,10 +630,10 @@ core unix memory profiler: TREZOR_MEMPERF: "1" PYTEST_TIMEOUT: "900" script: - - nix-shell --run "poetry run make -C core build_unix_frozen | ts -s" - - nix-shell --run "poetry run make -C core test_emu | ts -s" - - nix-shell --run "mkdir core/prof/memperf-html" - - nix-shell --run "poetry run core/tools/alloc.py --alloc-data=core/src/alloc_data.txt html core/prof/memperf-html" + - $NIX_SHELL --run "poetry run make -C core build_unix_frozen | ts -s" + - $NIX_SHELL --run "poetry run make -C core test_emu | ts -s" + - $NIX_SHELL --run "mkdir core/prof/memperf-html" + - $NIX_SHELL --run "poetry run core/tools/alloc.py --alloc-data=core/src/alloc_data.txt html core/prof/memperf-html" artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: @@ -647,6 +647,7 @@ core unix memory profiler: connect test core: image: ghcr.io/trezor/trezor-user-env stage: test + when: manual tags: - runner-internal needs: @@ -656,10 +657,10 @@ connect test core: before_script: - cp /builds/satoshilabs/trezor/trezor-firmware/core/build/unix/trezor-emu-core /trezor-user-env/src/binaries/firmware/bin/trezor-emu-core-v2.99.99 - chmod +x /trezor-user-env/src/binaries/firmware/bin/trezor-emu-core-v2.99.99 - - nix-shell --run "autoPatchelf /trezor-user-env/src/binaries/firmware/bin/trezor-emu-core-v2.99.99" + - $NIX_SHELL --run "autoPatchelf /trezor-user-env/src/binaries/firmware/bin/trezor-emu-core-v2.99.99" script: - /trezor-user-env/run-nix.sh & - - nix-shell --run "tests/connect_tests/connect_tests.sh 2.99.99" + - $NIX_SHELL --run "tests/connect_tests/connect_tests.sh 2.99.99" after_script: - cp /trezor-user-env/logs/debugging.log trezor-user-env-debugging.log - cp /trezor-user-env/logs/emulator_bridge.log tenv-emulator-bridge-debugging.log diff --git a/common/defs/blockchain_link.json b/common/defs/blockchain_link.json index 27d21ae428..20f80bad15 100644 --- a/common/defs/blockchain_link.json +++ b/common/defs/blockchain_link.json @@ -143,7 +143,7 @@ "bitcoin:SYS": { "type": "blockbook", "url": [ - "https://sys1.bcfn.ca" + "https://blockbook.elint.services" ] }, "bitcoin:TEST": { @@ -267,7 +267,7 @@ "misc:tADA": { "type": "blockfrost", "url": [ - "wss://trezor-cardano-testnet.blockfrost.io" + "wss://trezor-cardano-preview.blockfrost.io" ] } } diff --git a/common/defs/coins_details.json b/common/defs/coins_details.json index 2cbf44f2c6..aaaec9c987 100644 --- a/common/defs/coins_details.json +++ b/common/defs/coins_details.json @@ -5,7 +5,7 @@ "Github": "https://github.com/Actinium-project/Actinium", "Homepage": "https://actinium.org" }, - "marketcap_usd": 170785, + "marketcap_usd": 180870, "name": "Actinium", "shortcut": "ACM", "t1_enabled": "yes", @@ -23,7 +23,7 @@ "Github": "https://github.com/axerunners/axe", "Homepage": "https://axerunners.com" }, - "marketcap_usd": 33291, + "marketcap_usd": 20908, "name": "Axe", "shortcut": "AXE", "t1_enabled": "yes", @@ -38,10 +38,10 @@ }, "bitcoin:BCH": { "links": { - "Github": "https://github.com/Bitcoin-ABC/bitcoin-abc", + "Github": "https://github.com/bitcoin-cash-node/bitcoin-cash-node", "Homepage": "https://www.bitcoincash.org" }, - "marketcap_usd": 2218852104, + "marketcap_usd": 2567934801, "name": "Bitcoin Cash", "shortcut": "BCH", "t1_enabled": "yes", @@ -67,7 +67,7 @@ "Github": "https://github.com/bitcoin/bitcoin", "Homepage": "https://bitcoin.org" }, - "marketcap_usd": 391846686091, + "marketcap_usd": 451280107391, "name": "Bitcoin", "shortcut": "BTC", "t1_enabled": "yes", @@ -93,7 +93,7 @@ "Github": "https://github.com/BTCPrivate/BitcoinPrivate", "Homepage": "https://btcprivate.org" }, - "marketcap_usd": 3284351, + "marketcap_usd": 2118991, "name": "Bitcoin Private", "shortcut": "BTCP", "t1_enabled": "yes", @@ -111,7 +111,7 @@ "Github": "https://github.com/BTCGPU/BTCGPU", "Homepage": "https://bitcoingold.org" }, - "marketcap_usd": 302310527, + "marketcap_usd": 297235948, "name": "Bitcoin Gold", "shortcut": "BTG", "t1_enabled": "yes", @@ -137,7 +137,7 @@ "Github": "https://github.com/LIMXTEC/BitCore", "Homepage": "https://bitcore.cc" }, - "marketcap_usd": 913428, + "marketcap_usd": 1290295, "name": "Bitcore", "shortcut": "BTX", "t1_enabled": "yes", @@ -173,7 +173,7 @@ "Github": "https://github.com/Crowndev/crowncoin", "Homepage": "https://crownplatform.com" }, - "marketcap_usd": 407416, + "marketcap_usd": 227389, "name": "Crown", "shortcut": "CRW", "t1_enabled": "yes", @@ -191,7 +191,7 @@ "Github": "https://github.com/dashpay/dash", "Homepage": "https://www.dash.org" }, - "marketcap_usd": 446460854, + "marketcap_usd": 801190219, "name": "Dash", "shortcut": "DASH", "t1_enabled": "yes", @@ -221,7 +221,7 @@ "Github": "https://github.com/decred/dcrd", "Homepage": "https://www.decred.org" }, - "marketcap_usd": 387420904, + "marketcap_usd": 353035595, "name": "Decred", "shortcut": "DCR", "t1_enabled": "yes", @@ -239,7 +239,7 @@ "Github": "https://github.com/digibyte/digibyte", "Homepage": "https://digibyte.io" }, - "marketcap_usd": 136437999, + "marketcap_usd": 180666248, "name": "DigiByte", "shortcut": "DGB", "t1_enabled": "yes", @@ -261,7 +261,7 @@ "Github": "https://github.com/dogecoin/dogecoin", "Homepage": "https://dogecoin.com" }, - "marketcap_usd": 17458340960, + "marketcap_usd": 10815053466, "name": "Dogecoin", "shortcut": "DOGE", "t1_enabled": "yes", @@ -296,7 +296,7 @@ "Github": "https://github.com/firoorg/firo", "Homepage": "https://firo.org" }, - "marketcap_usd": 25525429, + "marketcap_usd": 30581066, "name": "Firo", "shortcut": "FIRO", "t1_enabled": "yes", @@ -354,7 +354,7 @@ "Github": "https://github.com/FeatherCoin/Feathercoin", "Homepage": "https://feathercoin.com" }, - "marketcap_usd": 1410060, + "marketcap_usd": 1216847, "name": "Feathercoin", "shortcut": "FTC", "t1_enabled": "yes", @@ -372,7 +372,7 @@ "Github": "https://github.com/Groestlcoin/groestlcoin", "Homepage": "https://www.groestlcoin.org" }, - "marketcap_usd": 29552741, + "marketcap_usd": 35812118, "name": "Groestlcoin", "shortcut": "GRS", "t1_enabled": "yes", @@ -390,7 +390,7 @@ "Github": "https://github.com/komodoplatform/komodo", "Homepage": "https://komodoplatform.com" }, - "marketcap_usd": 33418706, + "marketcap_usd": 40071524, "name": "Komodo", "shortcut": "KMD", "t1_enabled": "yes", @@ -421,7 +421,7 @@ "Github": "https://github.com/litecoin-project/litecoin", "Homepage": "https://litecoin.org" }, - "marketcap_usd": 4206955984, + "marketcap_usd": 6827206762, "name": "Litecoin", "shortcut": "LTC", "t1_enabled": "yes", @@ -447,7 +447,7 @@ "Github": "https://github.com/monacoinproject/monacoin", "Homepage": "https://monacoin.org" }, - "marketcap_usd": 29037582, + "marketcap_usd": 32996960, "name": "Monacoin", "shortcut": "MONA", "t1_enabled": "yes", @@ -483,7 +483,7 @@ "Github": "https://github.com/namecoin/namecoin-core", "Homepage": "https://namecoin.org" }, - "marketcap_usd": 16485813, + "marketcap_usd": 18207166, "name": "Namecoin", "shortcut": "NMC", "t1_enabled": "yes", @@ -505,7 +505,7 @@ "Github": "https://github.com/peercoin/peercoin", "Homepage": "https://peercoin.net" }, - "marketcap_usd": 13096518, + "marketcap_usd": 12956306, "name": "Peercoin", "shortcut": "PPC", "t1_enabled": "yes", @@ -518,7 +518,7 @@ "Github": "https://github.com/qtumproject/qtum", "Homepage": "https://qtum.org" }, - "marketcap_usd": 294077696, + "marketcap_usd": 349944403, "name": "Qtum", "shortcut": "QTUM", "t1_enabled": "yes", @@ -536,7 +536,7 @@ "Github": "https://github.com/RitoProject", "Homepage": "https://ritocoin.org" }, - "marketcap_usd": 70012, + "marketcap_usd": 41504, "name": "Ritocoin", "shortcut": "RITO", "t1_enabled": "yes", @@ -554,7 +554,7 @@ "Github": "https://github.com/RavenProject/Ravencoin", "Homepage": "https://ravencoin.org" }, - "marketcap_usd": 358925468, + "marketcap_usd": 364652876, "name": "Ravencoin", "shortcut": "RVN", "t1_enabled": "yes", @@ -576,7 +576,7 @@ "Github": "https://github.com/SmartCash/Core-Smart", "Homepage": "https://smartcash.cc" }, - "marketcap_usd": 574145, + "marketcap_usd": 831180, "name": "SmartCash", "shortcut": "SMART", "t1_enabled": "yes", @@ -594,7 +594,7 @@ "Github": "https://github.com/syscoin/syscoin", "Homepage": "https://syscoin.org" }, - "marketcap_usd": 99537142, + "marketcap_usd": 135689538, "name": "Syscoin", "shortcut": "SYS", "t1_enabled": "yes", @@ -630,7 +630,7 @@ "Github": "https://github.com/viacoin", "Homepage": "https://viacoin.org" }, - "marketcap_usd": 5676717, + "marketcap_usd": 1208142, "name": "Viacoin", "shortcut": "VIA", "t1_enabled": "yes", @@ -666,7 +666,7 @@ "Github": "https://github.com/vertcoin-project/vertcoin-core", "Homepage": "https://vertcoin.org" }, - "marketcap_usd": 11830934, + "marketcap_usd": 10390580, "name": "Vertcoin", "shortcut": "VTC", "t1_enabled": "yes", @@ -684,7 +684,7 @@ "Github": "https://github.com/primecoin/primecoin", "Homepage": "https://primecoin.io" }, - "marketcap_usd": 1591653, + "marketcap_usd": 1320490, "name": "Primecoin", "shortcut": "XPM", "t1_enabled": "yes", @@ -702,7 +702,7 @@ "Github": "https://gitlab.com/bitcoinrh/BRhodiumNode", "Homepage": "https://xrhodium.org" }, - "marketcap_usd": 292390, + "marketcap_usd": 162003, "name": "xRhodium", "shortcut": "XRC", "t1_enabled": "yes", @@ -738,7 +738,7 @@ "Github": "https://github.com/vergecurrency/VERGE", "Homepage": "https://vergecurrency.com" }, - "marketcap_usd": 51864312, + "marketcap_usd": 55431549, "name": "Verge", "shortcut": "XVG", "t1_enabled": "yes", @@ -756,7 +756,7 @@ "Github": "https://github.com/zcore-coin/zcore-2.0", "Homepage": "https://zcore.cash" }, - "marketcap_usd": 50663, + "marketcap_usd": 43805, "name": "ZCore", "shortcut": "ZCR", "t1_enabled": "yes", @@ -774,7 +774,7 @@ "Github": "https://github.com/zcash/zcash", "Homepage": "https://z.cash" }, - "marketcap_usd": 782825397, + "marketcap_usd": 719562172, "name": "Zcash", "shortcut": "ZEC", "t1_enabled": "yes", @@ -791,16 +791,15 @@ } ] }, - "erc20:avax:AVAX": { - "address": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", + "erc20:avax:USDT": { + "address": "0xde3A24028580884448a5397872046a019649b084", "links": { - "Github": "https://github.com/ava-labs", - "Homepage": "https://www.avalabs.org" + "Homepage": "https://tether.to/" }, "marketcap_usd": 0, - "name": "Avalanche", + "name": "Tether", "network": "avax", - "shortcut": "AVAX", + "shortcut": "USDT", "t1_enabled": "yes", "t2_enabled": "yes", "type": "erc20", @@ -815,15 +814,16 @@ } ] }, - "erc20:avax:USDT": { - "address": "0xde3A24028580884448a5397872046a019649b084", + "erc20:avax:WAVAX": { + "address": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", "links": { - "Homepage": "https://tether.to/" + "Github": "https://github.com/ava-labs", + "Homepage": "https://www.avalabs.org" }, "marketcap_usd": 0, - "name": "Tether", + "name": "Avalanche", "network": "avax", - "shortcut": "USDT", + "shortcut": "WAVAX", "t1_enabled": "yes", "t2_enabled": "yes", "type": "erc20", @@ -959,7 +959,7 @@ "links": { "Homepage": "https://0xbitcoin.org/" }, - "marketcap_usd": 1555936, + "marketcap_usd": 1723091, "name": "0xBitcoin", "network": "eth", "shortcut": "0xBTC", @@ -1017,7 +1017,7 @@ "links": { "Homepage": "https://ico.1worldonline.com" }, - "marketcap_usd": 3759768, + "marketcap_usd": 0, "name": "1World", "network": "eth", "shortcut": "1WO", @@ -1095,7 +1095,7 @@ "Github": "https://github.com/crypt04dvisor/AlphaWallet", "Homepage": "https://alphaplatform.co/" }, - "marketcap_usd": 276496, + "marketcap_usd": 139356, "name": "Alpha", "network": "eth", "shortcut": "A", @@ -1115,7 +1115,7 @@ "Github": "https://github.com/aave", "Homepage": "https://aave.com/ " }, - "marketcap_usd": 1161930750, + "marketcap_usd": 1111722344, "name": "Aave", "network": "eth", "shortcut": "AAVE", @@ -1194,7 +1194,7 @@ "links": { "Homepage": "https://www.arcblock.io" }, - "marketcap_usd": 11566295, + "marketcap_usd": 14156469, "name": "ArcBlock Token", "network": "eth", "shortcut": "ABT", @@ -1214,7 +1214,7 @@ "Github": "https://github.com/theabyssportal", "Homepage": "https://www.theabyss.com" }, - "marketcap_usd": 3732542, + "marketcap_usd": 3473031, "name": "Abyss Token", "network": "eth", "shortcut": "ABYSS", @@ -1271,7 +1271,7 @@ "links": { "Homepage": "https://adbank.network" }, - "marketcap_usd": 467096, + "marketcap_usd": 928468, "name": "adbank", "network": "eth", "shortcut": "ADB", @@ -1350,7 +1350,7 @@ "Github": "https://github.com/aditus", "Homepage": "https://aditus.net" }, - "marketcap_usd": 49258, + "marketcap_usd": 52124, "name": "Aditus", "network": "eth", "shortcut": "ADI", @@ -1423,26 +1423,6 @@ } ] }, - "erc20:eth:ADX": { - "address": "0xADE00C28244d5CE17D72E40330B1c318cD12B7c3", - "links": { - "Github": "https://github.com/AdExNetwork", - "Homepage": "https://www.adex.network" - }, - "marketcap_usd": 23515621, - "name": "AdEx Network", - "network": "eth", - "shortcut": "ADX", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": [ - { - "name": "Trezor Suite", - "url": "https://suite.trezor.io" - } - ] - }, "erc20:eth:ADX-LOYALTY": { "address": "0xd9A4cB9dc9296e111c66dFACAb8Be034EE2E1c2C", "links": { @@ -1463,26 +1443,6 @@ } ] }, - "erc20:eth:ADXL": { - "address": "0x4470BB87d77b963A013DB939BE332f927f2b992e", - "links": { - "Github": "https://github.com/AdExNetwork", - "Homepage": "https://www.adex.network" - }, - "marketcap_usd": 0, - "name": "AdEx Network (legacy)", - "network": "eth", - "shortcut": "ADXL", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": [ - { - "name": "Trezor Suite", - "url": "https://suite.trezor.io" - } - ] - }, "erc20:eth:AE": { "address": "0x5CA9a71B1d01849C0a95490Cc00559717fCF0D1d", "links": { @@ -1569,7 +1529,7 @@ "Github": "https://github.com/IDNI", "Homepage": "https://tau.net/" }, - "marketcap_usd": 3172747, + "marketcap_usd": 6463799, "name": "Agoras: Currency of Tau", "network": "eth", "shortcut": "AGRS", @@ -1627,7 +1587,7 @@ "links": { "Homepage": "https://www.aidcoin.co" }, - "marketcap_usd": 169103, + "marketcap_usd": 296328, "name": "AidCoin", "network": "eth", "shortcut": "AID", @@ -1686,7 +1646,7 @@ "Github": "https://github.com/AigangNetwork", "Homepage": "https://aigang.network/" }, - "marketcap_usd": 10160, + "marketcap_usd": 2866, "name": "Aigang", "network": "eth", "shortcut": "AIX", @@ -1745,7 +1705,7 @@ "Github": "https://github.com/aleph-im/", "Homepage": "https://aleph.im" }, - "marketcap_usd": 24352266, + "marketcap_usd": 17910289, "name": "Aleph.im", "network": "eth", "shortcut": "ALEPH", @@ -1922,7 +1882,7 @@ "Github": "https://github.com/amlt-by-coinfirm", "Homepage": "https://amlt.coinfirm.io/" }, - "marketcap_usd": 1048846, + "marketcap_usd": 1874483, "name": "AMLT", "network": "eth", "shortcut": "AMLT", @@ -1942,7 +1902,7 @@ "Github": "https://github.com/amontech", "Homepage": "https://amon.tech" }, - "marketcap_usd": 112369, + "marketcap_usd": 227037, "name": "Amon", "network": "eth", "shortcut": "AMN", @@ -1962,7 +1922,7 @@ "Github": "https://github.com/AMO-Project/", "Homepage": "https://amo.foundation" }, - "marketcap_usd": 19117921, + "marketcap_usd": 15060121, "name": "AMO Coin", "network": "eth", "shortcut": "AMO", @@ -1981,7 +1941,7 @@ "links": { "Homepage": "https://ados.foundation/" }, - "marketcap_usd": 362721, + "marketcap_usd": 709645, "name": "Token AmonD", "network": "eth", "shortcut": "AMON", @@ -2001,7 +1961,7 @@ "Github": "https://github.com/amptoken", "Homepage": "https://amptoken.org" }, - "marketcap_usd": 203029513, + "marketcap_usd": 238924137, "name": "Amp", "network": "eth", "shortcut": "AMP", @@ -2021,7 +1981,7 @@ "Github": "https://github.com/ampleforth", "Homepage": "https://ampleforth.org" }, - "marketcap_usd": 51073131, + "marketcap_usd": 46578948, "name": "Ampleforth", "network": "eth", "shortcut": "AMPL", @@ -2099,7 +2059,7 @@ "links": { "Homepage": "https://www.aurorachain.io" }, - "marketcap_usd": 2226265, + "marketcap_usd": 2078947, "name": "Aurora", "network": "eth", "shortcut": "AOA", @@ -2119,7 +2079,7 @@ "Github": "https://github.com/api3dao", "Homepage": "https://api3.org/" }, - "marketcap_usd": 100290169, + "marketcap_usd": 124100419, "name": "API3", "network": "eth", "shortcut": "API3", @@ -2198,7 +2158,7 @@ "Github": "https://github.com/alphaquark/Alpha-Quark", "Homepage": "https://www.alphaquark.io" }, - "marketcap_usd": 28613935, + "marketcap_usd": 33804550, "name": "AlphaQuarkToken", "network": "eth", "shortcut": "AQT", @@ -2237,7 +2197,7 @@ "links": { "Homepage": "https://www.arbitragect.com" }, - "marketcap_usd": 21363, + "marketcap_usd": 48931, "name": "ArbitrageCT", "network": "eth", "shortcut": "ARCT", @@ -2316,7 +2276,7 @@ "Github": "https://github.com/aeronaero/aeron", "Homepage": "https://aeron.aero" }, - "marketcap_usd": 366, + "marketcap_usd": 95, "name": "Aeron", "network": "eth", "shortcut": "ARNX", @@ -2335,7 +2295,7 @@ "links": { "Homepage": "http://www.maecenas.co" }, - "marketcap_usd": 44657, + "marketcap_usd": 41649, "name": "Maecenas", "network": "eth", "shortcut": "ART", @@ -2413,7 +2373,7 @@ "links": { "Homepage": "https://airswap.io" }, - "marketcap_usd": 22471693, + "marketcap_usd": 19734634, "name": "Airswap", "network": "eth", "shortcut": "AST", @@ -2452,7 +2412,7 @@ "links": { "Homepage": "https://atlant.io" }, - "marketcap_usd": 598005, + "marketcap_usd": 638027, "name": "ATLANT", "network": "eth", "shortcut": "ATL", @@ -2568,7 +2528,7 @@ "links": { "Homepage": "https://auctus.org" }, - "marketcap_usd": 113891, + "marketcap_usd": 141310, "name": "Auctus", "network": "eth", "shortcut": "AUC", @@ -2588,7 +2548,7 @@ "Github": "https://github.com/audiusproject", "Homepage": "https://audius.co" }, - "marketcap_usd": 184541037, + "marketcap_usd": 323841415, "name": "Audius", "network": "eth", "shortcut": "AUDIO", @@ -2685,7 +2645,7 @@ "links": { "Homepage": "https://aventus.io" }, - "marketcap_usd": 9322687, + "marketcap_usd": 7052932, "name": "Aventus", "network": "eth", "shortcut": "AVT", @@ -2784,7 +2744,7 @@ "Github": "https://github.com/axieinfinity", "Homepage": "https://axieinfinity.com/" }, - "marketcap_usd": 841323253, + "marketcap_usd": 1098056194, "name": "Axie Infinity Shards", "network": "eth", "shortcut": "AXS", @@ -2843,7 +2803,7 @@ "Github": "https://github.com/balancer-labs", "Homepage": "https://balancer.finance" }, - "marketcap_usd": 296900138, + "marketcap_usd": 329982207, "name": "Balancer", "network": "eth", "shortcut": "BAL", @@ -2902,7 +2862,7 @@ "links": { "Homepage": "https://www.banca.world" }, - "marketcap_usd": 260806, + "marketcap_usd": 152329, "name": "Banca", "network": "eth", "shortcut": "BANCA", @@ -2960,7 +2920,7 @@ "links": { "Homepage": "https://basicattentiontoken.org" }, - "marketcap_usd": 481787663, + "marketcap_usd": 442713735, "name": "Basic Attention Token", "network": "eth", "shortcut": "BAT", @@ -3154,7 +3114,7 @@ "Github": "https://github.com/VinceBCD/BCDiploma", "Homepage": "https://www.bcdiploma.com" }, - "marketcap_usd": 2752949, + "marketcap_usd": 2474009, "name": "Blockchain Certified Data Token", "network": "eth", "shortcut": "BCDT", @@ -3212,7 +3172,7 @@ "Github": "https://github.com/blockmason", "Homepage": "https://blockmason.io" }, - "marketcap_usd": 296795, + "marketcap_usd": 285234, "name": "BlockMason Credit Protocol Token", "network": "eth", "shortcut": "BCPT", @@ -3232,7 +3192,7 @@ "Github": "https://github.com/bitcv", "Homepage": "https://bitcv.one/" }, - "marketcap_usd": 135719, + "marketcap_usd": 0, "name": "BitCapitalVendor Token", "network": "eth", "shortcut": "BCV", @@ -3331,7 +3291,7 @@ "Github": "https://github.com/Rentberry", "Homepage": "https://rentberry.com" }, - "marketcap_usd": 88451, + "marketcap_usd": 107070, "name": "Berry", "network": "eth", "shortcut": "BERRY", @@ -3429,7 +3389,7 @@ "links": { "Homepage": "https://bnktothefuture.com" }, - "marketcap_usd": 5865681, + "marketcap_usd": 3387108, "name": "BnkToTheFuture", "network": "eth", "shortcut": "BFT", @@ -3701,7 +3661,7 @@ "Github": "https://github.com/hellobloom", "Homepage": "https://hellobloom.io" }, - "marketcap_usd": 1697321, + "marketcap_usd": 3006871, "name": "Bloom", "network": "eth", "shortcut": "BLT", @@ -3721,7 +3681,7 @@ "Github": "https://github.com/BlueCrypto", "Homepage": "https://blueprotocol.com/" }, - "marketcap_usd": 184442, + "marketcap_usd": 234732, "name": "Ethereum Blue", "network": "eth", "shortcut": "BLUE", @@ -3779,7 +3739,7 @@ "links": { "Homepage": "https://bluzelle.com" }, - "marketcap_usd": 25431290, + "marketcap_usd": 40249218, "name": "Bluzelle", "network": "eth", "shortcut": "BLZ", @@ -3818,7 +3778,7 @@ "links": { "Homepage": "https://www.bitmart.com" }, - "marketcap_usd": 25357237, + "marketcap_usd": 18049390, "name": "BitMart Token", "network": "eth", "shortcut": "BMX", @@ -3837,7 +3797,7 @@ "links": { "Homepage": "https://www.binance.com" }, - "marketcap_usd": 51094687147, + "marketcap_usd": 0, "name": "Binance Coin", "network": "eth", "shortcut": "BNB", @@ -3896,7 +3856,7 @@ "Github": "https://github.com/bancorprotocol", "Homepage": "https://www.bancor.network" }, - "marketcap_usd": 92685313, + "marketcap_usd": 74533517, "name": "Bancor Network Token", "network": "eth", "shortcut": "BNT", @@ -3915,7 +3875,7 @@ "links": { "Homepage": "https://bounty0x.io" }, - "marketcap_usd": 219819, + "marketcap_usd": 148222, "name": "Bounty0x Token", "network": "eth", "shortcut": "BNTY", @@ -3934,7 +3894,7 @@ "links": { "Homepage": "https://bobsrepair.com" }, - "marketcap_usd": 320390, + "marketcap_usd": 710785, "name": "Bob's repair", "network": "eth", "shortcut": "BOB", @@ -4011,7 +3971,7 @@ "links": { "Homepage": "https://bonpay.com" }, - "marketcap_usd": 6913, + "marketcap_usd": 7918, "name": "Bonpay", "network": "eth", "shortcut": "BON", @@ -4068,7 +4028,7 @@ "links": { "Homepage": "https://www.bouts.pro" }, - "marketcap_usd": 16672, + "marketcap_usd": 13777, "name": "BoutsPro", "network": "eth", "shortcut": "BOUTS", @@ -4185,7 +4145,7 @@ "Github": "https://github.com/breadwallet", "Homepage": "https://token.breadapp.com/en" }, - "marketcap_usd": 818082, + "marketcap_usd": 544089, "name": "Bread", "network": "eth", "shortcut": "BRD", @@ -4517,7 +4477,7 @@ "links": { "Homepage": "https://www.bottos.org" }, - "marketcap_usd": 273933, + "marketcap_usd": 464164, "name": "Bottos", "network": "eth", "shortcut": "BTO", @@ -4654,7 +4614,7 @@ "Github": "https://github.com/paxosglobal/busd-contract", "Homepage": "https://www.paxos.com/busd" }, - "marketcap_usd": 21655558969, + "marketcap_usd": 10638728787, "name": "Binance USD (BUSD)", "network": "eth", "shortcut": "BUSD", @@ -4731,7 +4691,7 @@ "links": { "Homepage": "https://bezant.io" }, - "marketcap_usd": 342268, + "marketcap_usd": 328936, "name": "Bezant", "network": "eth", "shortcut": "BZNT", @@ -4771,7 +4731,7 @@ "Github": "https://github.com/cryptotwenty", "Homepage": "https://crypto20.com" }, - "marketcap_usd": 1369992, + "marketcap_usd": 1040095, "name": "Crypto20's Token", "network": "eth", "shortcut": "C20", @@ -4829,7 +4789,7 @@ "Github": "https://github.com/Global-Crypto-Alliance/call-token", "Homepage": "https://gcalliance.io" }, - "marketcap_usd": 95664, + "marketcap_usd": 0, "name": "CALL token", "network": "eth", "shortcut": "CALL", @@ -4943,7 +4903,7 @@ "links": { "Homepage": "https://coin.cashbet.com" }, - "marketcap_usd": 1382293, + "marketcap_usd": 1412688, "name": "CashBet Coin", "network": "eth", "shortcut": "CBC", @@ -5059,7 +5019,7 @@ "links": { "Homepage": "https://ccore.io" }, - "marketcap_usd": 14839, + "marketcap_usd": 15877, "name": "Ccore", "network": "eth", "shortcut": "CCO", @@ -5117,7 +5077,7 @@ "links": { "Homepage": "https://www.ceek.com/" }, - "marketcap_usd": 109692246, + "marketcap_usd": 85892279, "name": "CEEK VR Token", "network": "eth", "shortcut": "CEEK", @@ -5137,7 +5097,7 @@ "Github": "https://github.com/celer-network", "Homepage": "https://www.celer.network/" }, - "marketcap_usd": 101777758, + "marketcap_usd": 145988517, "name": "CelerToken", "network": "eth", "shortcut": "CELR", @@ -5176,7 +5136,7 @@ "links": { "Homepage": "https://www.centrality.ai" }, - "marketcap_usd": 25642589, + "marketcap_usd": 20808822, "name": "Centrality", "network": "eth", "shortcut": "CENNZ", @@ -5274,7 +5234,7 @@ "Github": "https://github.com/cache-token/cache-contract", "Homepage": "https://cache.gold" }, - "marketcap_usd": 3672269, + "marketcap_usd": 2889849, "name": "CACHE Gold", "network": "eth", "shortcut": "CGT", @@ -5332,7 +5292,7 @@ "links": { "Homepage": "https://swissborg.com" }, - "marketcap_usd": 236704686, + "marketcap_usd": 214599152, "name": "SwissBorg", "network": "eth", "shortcut": "CHSB", @@ -5371,7 +5331,7 @@ "Github": "https://github.com/CivilizationCIV", "Homepage": "https://civfund.org" }, - "marketcap_usd": 7556958, + "marketcap_usd": 9218468, "name": "Civilization", "network": "eth", "shortcut": "CIV", @@ -5546,7 +5506,7 @@ "links": { "Homepage": "https://coinloan.io" }, - "marketcap_usd": 30997570, + "marketcap_usd": 0, "name": "CoinLoan", "network": "eth", "shortcut": "CLT", @@ -5622,7 +5582,7 @@ "links": { "Homepage": "https://app.coinmerge.io/" }, - "marketcap_usd": 1398993, + "marketcap_usd": 0, "name": "Coin Merge", "network": "eth", "shortcut": "CMERGE", @@ -5660,7 +5620,7 @@ "links": { "Homepage": "https://cindicator.com" }, - "marketcap_usd": 1186005, + "marketcap_usd": 2370802, "name": "Cindicator", "network": "eth", "shortcut": "CND", @@ -5679,7 +5639,7 @@ "links": { "Homepage": "https://cnntoken.io" }, - "marketcap_usd": 129880, + "marketcap_usd": 0, "name": "Content Neutrality Network", "network": "eth", "shortcut": "CNN", @@ -5738,7 +5698,7 @@ "Github": "https://github.com/cobinhood", "Homepage": "https://cobinhood.com" }, - "marketcap_usd": 83896, + "marketcap_usd": 96081, "name": "Cobinhood Token", "network": "eth", "shortcut": "COB", @@ -5778,7 +5738,7 @@ "Github": "https://github.com/coinfi", "Homepage": "https://www.coinfi.com" }, - "marketcap_usd": 367774, + "marketcap_usd": 376891, "name": "CoinFi Token", "network": "eth", "shortcut": "COFI", @@ -5818,7 +5778,7 @@ "Github": "https://github.com/compound-finance", "Homepage": "https://compound.finance" }, - "marketcap_usd": 340322043, + "marketcap_usd": 361396714, "name": "Compound", "network": "eth", "shortcut": "COMP", @@ -5876,7 +5836,7 @@ "Github": "https://github.com/coti-io", "Homepage": "https://coti.io" }, - "marketcap_usd": 109304001, + "marketcap_usd": 101905846, "name": "COTI", "network": "eth", "shortcut": "COTI", @@ -5953,7 +5913,7 @@ "links": { "Homepage": "http://www.cpchain.io" }, - "marketcap_usd": 1017265, + "marketcap_usd": 1798478, "name": "CPChain", "network": "eth", "shortcut": "CPC", @@ -6206,7 +6166,7 @@ "links": { "Homepage": "https://credits.com/en" }, - "marketcap_usd": 821955, + "marketcap_usd": 1464121, "name": "Credits", "network": "eth", "shortcut": "CS", @@ -6324,7 +6284,7 @@ "Github": "https://github.com/cartesi", "Homepage": "https://cartesi.io" }, - "marketcap_usd": 86108834, + "marketcap_usd": 108034607, "name": "Cartesi Token", "network": "eth", "shortcut": "CTSI", @@ -6420,7 +6380,7 @@ "links": { "Homepage": "https://www.civic.com" }, - "marketcap_usd": 119866737, + "marketcap_usd": 118856816, "name": "Civic", "network": "eth", "shortcut": "CVC", @@ -6459,7 +6419,7 @@ "links": { "Homepage": "http://www.cybervein.org" }, - "marketcap_usd": 1471294, + "marketcap_usd": 1632453, "name": "CyberVein", "network": "eth", "shortcut": "CVT", @@ -6557,7 +6517,7 @@ "links": { "Homepage": "https://cybermusic.io" }, - "marketcap_usd": 123113, + "marketcap_usd": 252201, "name": "CyberMusic", "network": "eth", "shortcut": "CYMT", @@ -6693,7 +6653,7 @@ "Github": "https://github.com/makerdao", "Homepage": "https://makerdao.com" }, - "marketcap_usd": 6114390089, + "marketcap_usd": 5224409492, "name": "Dai Stablecoin v2.0", "network": "eth", "shortcut": "DAI", @@ -6765,6 +6725,26 @@ } ] }, + "erc20:eth:DATA": { + "address": "0x8f693ca8D21b157107184d29D398A8D082b38b76", + "links": { + "Github": "https://github.com/streamr-dev", + "Homepage": "https://streamr.network/" + }, + "marketcap_usd": 31983242, + "name": "Streamr (DATA)", + "network": "eth", + "shortcut": "DATA", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "erc20", + "wallet": [ + { + "name": "Trezor Suite", + "url": "https://suite.trezor.io" + } + ] + }, "erc20:eth:DATABroker": { "address": "0x1B5f21ee98eed48d292e8e2d3Ed82b40a9728A22", "links": { @@ -6810,7 +6790,7 @@ "Github": "https://github.com/DAVFoundation", "Homepage": "https://dav.network/" }, - "marketcap_usd": 1455080, + "marketcap_usd": 713066, "name": "DAV Token", "network": "eth", "shortcut": "DAV", @@ -6829,7 +6809,7 @@ "links": { "Homepage": "https://www.daex.io" }, - "marketcap_usd": 1067349, + "marketcap_usd": 2068186, "name": "DAEX", "network": "eth", "shortcut": "DAX", @@ -6868,7 +6848,7 @@ "Github": "https://github.com/chronologic", "Homepage": "https://chronologic.network" }, - "marketcap_usd": 28050, + "marketcap_usd": 0, "name": "ChronoLogic DAY", "network": "eth", "shortcut": "DAY", @@ -6887,7 +6867,7 @@ "links": { "Homepage": "https://www.decent.bet" }, - "marketcap_usd": 38505, + "marketcap_usd": 61736, "name": "DecentBet", "network": "eth", "shortcut": "DBET", @@ -6986,7 +6966,7 @@ "Github": "https://github.com/Dentacoin", "Homepage": "https://dentacoin.com" }, - "marketcap_usd": 1652451, + "marketcap_usd": 1239469, "name": "Dentacoin", "network": "eth", "shortcut": "DCN", @@ -7083,7 +7063,7 @@ "links": { "Homepage": "https://www.dentwireless.com" }, - "marketcap_usd": 83547899, + "marketcap_usd": 116975263, "name": "DENT", "network": "eth", "shortcut": "DENT", @@ -7141,7 +7121,7 @@ "Github": "https://github.com/dforcenetwork", "Homepage": "https://dforce.network" }, - "marketcap_usd": 20134820, + "marketcap_usd": 23334461, "name": "dForce Platform Token", "network": "eth", "shortcut": "DF", @@ -7160,7 +7140,7 @@ "links": { "Homepage": "https://digix.global/" }, - "marketcap_usd": 11955948, + "marketcap_usd": 8201143, "name": "Digix DAO", "network": "eth", "shortcut": "DGD", @@ -7220,7 +7200,7 @@ "Github": "https://github.com/DigixGlobal", "Homepage": "https://digix.global" }, - "marketcap_usd": 2070563, + "marketcap_usd": 2548238, "name": "Digix Gold Token", "network": "eth", "shortcut": "DGX", @@ -7298,7 +7278,7 @@ "links": { "Homepage": "https://inmediate.io" }, - "marketcap_usd": 330096, + "marketcap_usd": 0, "name": "Digital Insurance Token", "network": "eth", "shortcut": "DIT", @@ -7336,7 +7316,7 @@ "links": { "Homepage": "https://www.agrello.org" }, - "marketcap_usd": 105387, + "marketcap_usd": 135781, "name": "Agrello", "network": "eth", "shortcut": "DLT", @@ -7356,7 +7336,7 @@ "Github": "https://github.com/suntechsoft/dmarket-smartcontract", "Homepage": "https://dmarket.com" }, - "marketcap_usd": 395055, + "marketcap_usd": 399206, "name": "DMarket Token", "network": "eth", "shortcut": "DMT", @@ -7395,7 +7375,7 @@ "Github": "https://github.com/district0x", "Homepage": "https://district0x.io" }, - "marketcap_usd": 16254401, + "marketcap_usd": 21489851, "name": "District0x Network Token", "network": "eth", "shortcut": "DNT", @@ -7491,7 +7471,7 @@ "links": { "Homepage": "https://dovu.io" }, - "marketcap_usd": 2494127, + "marketcap_usd": 10302838, "name": "Dovu", "network": "eth", "shortcut": "DOV", @@ -7550,7 +7530,7 @@ "Github": "https://github.com/dragonchain/dragonchain", "Homepage": "https://dragonchain.com" }, - "marketcap_usd": 5681767, + "marketcap_usd": 5066629, "name": "Dragon", "network": "eth", "shortcut": "DRGN", @@ -7588,7 +7568,7 @@ "links": { "Homepage": "https://token.domraider.com" }, - "marketcap_usd": 190033, + "marketcap_usd": 138277, "name": "DomRaider", "network": "eth", "shortcut": "DRT", @@ -7725,7 +7705,7 @@ "links": { "Homepage": "https://datarius.io" }, - "marketcap_usd": 41183, + "marketcap_usd": 37098, "name": "Datarius Credit", "network": "eth", "shortcut": "DTRC", @@ -7976,7 +7956,7 @@ "links": { "Homepage": "https://edgeless.io" }, - "marketcap_usd": 4520365, + "marketcap_usd": 1489622, "name": "Edgeless", "network": "eth", "shortcut": "EDG", @@ -8095,7 +8075,7 @@ "Github": "https://github.com/egretia", "Homepage": "https://www.egretia.io" }, - "marketcap_usd": 1508009, + "marketcap_usd": 1382423, "name": "Egretia Token", "network": "eth", "shortcut": "EGT", @@ -8153,7 +8133,7 @@ "links": { "Homepage": "https://echolink.info" }, - "marketcap_usd": 34050, + "marketcap_usd": 16271, "name": "EchoLink", "network": "eth", "shortcut": "EKO", @@ -8191,7 +8171,7 @@ "links": { "Homepage": "https://electrify.asia" }, - "marketcap_usd": 527477, + "marketcap_usd": 385598, "name": "Electrify.Asia", "network": "eth", "shortcut": "ELEC", @@ -8211,7 +8191,7 @@ "Github": "https://github.com/aelfProject", "Homepage": "https://aelf.io/" }, - "marketcap_usd": 67897092, + "marketcap_usd": 157530461, "name": "ELF Token", "network": "eth", "shortcut": "ELF", @@ -8250,7 +8230,7 @@ "Github": "https://github.com/eltcoin", "Homepage": "http://www.eltcoin.tech/" }, - "marketcap_usd": 23680, + "marketcap_usd": 29287, "name": "ELTCOIN", "network": "eth", "shortcut": "ELTCOIN", @@ -8270,7 +8250,7 @@ "Github": "https://github.com/Elysian-ELY", "Homepage": "https://elycoin.io" }, - "marketcap_usd": 33357, + "marketcap_usd": 41694, "name": "ELYCOIN", "network": "eth", "shortcut": "ELY", @@ -8408,7 +8388,7 @@ "Github": "https://github.com/enigmampc", "Homepage": "https://enigma.co/" }, - "marketcap_usd": 117358, + "marketcap_usd": 66749, "name": "Enigma", "network": "eth", "shortcut": "ENG", @@ -8447,7 +8427,7 @@ "Github": "https://github.com/enjin/contracts", "Homepage": "https://enjincoin.io" }, - "marketcap_usd": 432291943, + "marketcap_usd": 468951418, "name": "ENJIN", "network": "eth", "shortcut": "ENJ", @@ -8467,7 +8447,7 @@ "Github": "https://github.com/Enecuum", "Homepage": "https://enecuum.com" }, - "marketcap_usd": 7945051, + "marketcap_usd": 3492342, "name": "Enecuum", "network": "eth", "shortcut": "ENQ", @@ -8584,7 +8564,7 @@ "links": { "Homepage": "https://eroscoin.org" }, - "marketcap_usd": 38119, + "marketcap_usd": 43656, "name": "Eroscoin", "network": "eth", "shortcut": "ERO", @@ -8838,7 +8818,7 @@ "Github": "https://github.com/stasisnet", "Homepage": "https://stasis.net" }, - "marketcap_usd": 110532336, + "marketcap_usd": 130380616, "name": "STASIS EURS", "network": "eth", "shortcut": "EURS", @@ -8876,7 +8856,7 @@ "links": { "Homepage": "https://eventchain.io" }, - "marketcap_usd": 32588, + "marketcap_usd": 26440, "name": "EventChain", "network": "eth", "shortcut": "EVC", @@ -8935,7 +8915,7 @@ "Github": "https://github.com/evedo-co", "Homepage": "https://www.evedo.co" }, - "marketcap_usd": 419149, + "marketcap_usd": 394579, "name": "Evedo Token", "network": "eth", "shortcut": "EVED", @@ -8994,7 +8974,7 @@ "links": { "Homepage": "https://everex.io " }, - "marketcap_usd": 264591, + "marketcap_usd": 338085, "name": "Everex", "network": "eth", "shortcut": "EVX", @@ -9092,7 +9072,7 @@ "links": { "Homepage": "https://exrnchain.com" }, - "marketcap_usd": 1431650, + "marketcap_usd": 1505076, "name": "EXRNchain", "network": "eth", "shortcut": "EXRN", @@ -9208,7 +9188,7 @@ "links": { "Homepage": "https://tokensale.faceter.io" }, - "marketcap_usd": 207152, + "marketcap_usd": 0, "name": "Faceter", "network": "eth", "shortcut": "FACE", @@ -9306,7 +9286,7 @@ "links": { "Homepage": "https://friendz.io" }, - "marketcap_usd": 199716, + "marketcap_usd": 22710, "name": "Friendz", "network": "eth", "shortcut": "FDZ", @@ -9444,7 +9424,7 @@ "links": { "Homepage": "https://www.flixxo.com" }, - "marketcap_usd": 159183, + "marketcap_usd": 739558, "name": "Flixxo", "network": "eth", "shortcut": "FLIXX", @@ -9463,7 +9443,7 @@ "links": { "Homepage": "https://firelotto.io" }, - "marketcap_usd": 16070, + "marketcap_usd": 32076, "name": "Fire Lotto", "network": "eth", "shortcut": "FLOT", @@ -9483,7 +9463,7 @@ "Github": "https://github.com/gameflip", "Homepage": "https://gameflip.com" }, - "marketcap_usd": 469995, + "marketcap_usd": 439780, "name": "FLIP Token", "network": "eth", "shortcut": "FLP", @@ -9639,7 +9619,7 @@ "Github": "https://github.com/f-o-a-m", "Homepage": "http://foam.space" }, - "marketcap_usd": 8465344, + "marketcap_usd": 4813660, "name": "FOAM Token", "network": "eth", "shortcut": "FOAM", @@ -9716,7 +9696,7 @@ "links": { "Homepage": "https://shapeshift.com" }, - "marketcap_usd": 14988331, + "marketcap_usd": 14439774, "name": "FOX", "network": "eth", "shortcut": "FOX", @@ -9852,7 +9832,7 @@ "links": { "Homepage": "https://fanstime.org" }, - "marketcap_usd": 176143, + "marketcap_usd": 147781, "name": "FansTime", "network": "eth", "shortcut": "FTI", @@ -9872,7 +9852,7 @@ "Github": "https://github.com/Fantom-foundation/", "Homepage": "https://fantom.foundation/" }, - "marketcap_usd": 574037369, + "marketcap_usd": 1245702872, "name": "Fantom Token", "network": "eth", "shortcut": "FTM", @@ -9930,7 +9910,7 @@ "links": { "Homepage": "https://www.fintrux.com" }, - "marketcap_usd": 449697, + "marketcap_usd": 176615, "name": "FintruX Network", "network": "eth", "shortcut": "FTX", @@ -9950,7 +9930,7 @@ "Github": "https://github.com/futuraxproject", "Homepage": "https://futurax.global" }, - "marketcap_usd": 52722, + "marketcap_usd": 55426, "name": "FUTURAX", "network": "eth", "shortcut": "FTXT", @@ -9970,7 +9950,7 @@ "Github": "https://github.com/etherparty", "Homepage": "https://etherparty.io" }, - "marketcap_usd": 270613, + "marketcap_usd": 265967, "name": "Etherparty FUEL", "network": "eth", "shortcut": "FUEL", @@ -9989,7 +9969,7 @@ "links": { "Homepage": "https://funfair.io" }, - "marketcap_usd": 82431215, + "marketcap_usd": 77061620, "name": "Funfair", "network": "eth", "shortcut": "FUN", @@ -10008,7 +9988,7 @@ "links": { "Homepage": "https://fuzex.co" }, - "marketcap_usd": 39376, + "marketcap_usd": 0, "name": "FuzeX", "network": "eth", "shortcut": "FXT", @@ -10066,7 +10046,7 @@ "links": { "Homepage": "https://flyp.me" }, - "marketcap_usd": 512140, + "marketcap_usd": 618522, "name": "FlypMe", "network": "eth", "shortcut": "FYP", @@ -10125,7 +10105,7 @@ "Github": "https://github.com/gluwa/Creditcoin", "Homepage": "https://creditcoinfoundation.org" }, - "marketcap_usd": 117187195, + "marketcap_usd": 103231356, "name": "Creditcoin Token", "network": "eth", "shortcut": "G-CRE", @@ -10165,7 +10145,7 @@ "Github": "https://github.com/gamecredits-project", "Homepage": "https://www.gamecredits.org" }, - "marketcap_usd": 2462960, + "marketcap_usd": 3159640, "name": "GAME Credits", "network": "eth", "shortcut": "GAME", @@ -10400,7 +10380,7 @@ "Github": "https://github.com/Governor-DAO", "Homepage": "https://www.governordao.org" }, - "marketcap_usd": 502739, + "marketcap_usd": 362122, "name": "Governor DAO", "network": "eth", "shortcut": "GDAO", @@ -10478,7 +10458,7 @@ "links": { "Homepage": "https://gems.org" }, - "marketcap_usd": 142792, + "marketcap_usd": 94104, "name": "Gems", "network": "eth", "shortcut": "GEM", @@ -10498,7 +10478,7 @@ "Github": "https://github.com/daostack", "Homepage": "https://daostack.io" }, - "marketcap_usd": 437613, + "marketcap_usd": 1374362, "name": "DAOstack", "network": "eth", "shortcut": "GEN", @@ -10537,7 +10517,7 @@ "Github": "https://github.com/Getprotocol", "Homepage": "http://www.get-protocol.io" }, - "marketcap_usd": 12604913, + "marketcap_usd": 17570214, "name": "GET Protocol", "network": "eth", "shortcut": "GET", @@ -10595,7 +10575,7 @@ "links": { "Homepage": "https://gamerhash.io/" }, - "marketcap_usd": 7810604, + "marketcap_usd": 5973218, "name": "GamerCoin", "network": "eth", "shortcut": "GHX", @@ -10668,6 +10648,26 @@ } ] }, + "erc20:eth:GIV": { + "address": "0x900dB999074d9277c5DA2A43F252D74366230DA0", + "links": { + "Github": "https://github.com/Giveth", + "Homepage": "https://giveth.io/" + }, + "marketcap_usd": 0, + "name": "Giveth", + "network": "eth", + "shortcut": "GIV", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "erc20", + "wallet": [ + { + "name": "Trezor Suite", + "url": "https://suite.trezor.io" + } + ] + }, "erc20:eth:GL": { "address": "0xA5B399a76bbAbEf93D70255525C1d2BCC3701d0b", "links": { @@ -10712,7 +10712,7 @@ "links": { "Homepage": "https://gamb.io" }, - "marketcap_usd": 968385, + "marketcap_usd": 1277478, "name": "GAMB", "network": "eth", "shortcut": "GMB", @@ -10731,7 +10731,7 @@ "links": { "Homepage": "https://gnosis.pm" }, - "marketcap_usd": 301224889, + "marketcap_usd": 278912676, "name": "Gnosis", "network": "eth", "shortcut": "GNO", @@ -10770,7 +10770,7 @@ "links": { "Homepage": "https://genaro.network" }, - "marketcap_usd": 1885910, + "marketcap_usd": 1942086, "name": "Genaro Network", "network": "eth", "shortcut": "GNX", @@ -10849,7 +10849,7 @@ "Github": "https://github.com/coti-io/cvi-contracts", "Homepage": "https://cvi.finance" }, - "marketcap_usd": 4959922, + "marketcap_usd": 9674350, "name": "GOVI", "network": "eth", "shortcut": "GOVI", @@ -10887,7 +10887,7 @@ "links": { "Homepage": "http://gridplus.io" }, - "marketcap_usd": 11336358, + "marketcap_usd": 8168623, "name": "Grid+", "network": "eth", "shortcut": "GRID", @@ -10966,7 +10966,7 @@ "Github": "https://github.com/graphprotocol", "Homepage": "https://thegraph.com" }, - "marketcap_usd": 560119157, + "marketcap_usd": 1397892462, "name": "Graph Token", "network": "eth", "shortcut": "GRT", @@ -10985,7 +10985,7 @@ "links": { "Homepage": "https://www.gsc.social" }, - "marketcap_usd": 383092, + "marketcap_usd": 1307261, "name": "Global Social Chain", "network": "eth", "shortcut": "GSC", @@ -11024,7 +11024,7 @@ "Github": "https://github.com/GameLeLe", "Homepage": "https://game.com" }, - "marketcap_usd": 597721, + "marketcap_usd": 626409, "name": "GTC Token", "network": "eth", "shortcut": "GTC", @@ -11038,13 +11038,33 @@ } ] }, + "erc20:eth:GTEC": { + "address": "0x30E193bd3F52713D5562cf316f35115034525f44", + "links": { + "Github": "https://github.com/Sustainability-Creative-Co/GreenTechCoin", + "Homepage": "https://greentechcoin.xyz" + }, + "marketcap_usd": 0, + "name": "Green Tech Coin", + "network": "eth", + "shortcut": "GTEC", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "erc20", + "wallet": [ + { + "name": "Trezor Suite", + "url": "https://suite.trezor.io" + } + ] + }, "erc20:eth:GTH": { "address": "0xeb986DA994E4a118d5956b02d8b7c3C7CE373674", "links": { "Github": "https://github.com/GatherNetwork", "Homepage": "https://gather.network" }, - "marketcap_usd": 1283292, + "marketcap_usd": 1331693, "name": "GTH", "network": "eth", "shortcut": "GTH", @@ -11084,7 +11104,7 @@ "Github": "https://github.com/GIFTO-io", "Homepage": "https://gifto.io/" }, - "marketcap_usd": 18131381, + "marketcap_usd": 0, "name": "Gifto", "network": "eth", "shortcut": "GTO", @@ -11162,7 +11182,7 @@ "Github": "https://github.com/GenesisVision", "Homepage": "https://genesis.vision" }, - "marketcap_usd": 764941, + "marketcap_usd": 849673, "name": "Genesis Vision", "network": "eth", "shortcut": "GVT", @@ -11338,7 +11358,7 @@ "links": { "Homepage": "https://www.showhand.io" }, - "marketcap_usd": 958237, + "marketcap_usd": 1007382, "name": "ShowHand", "network": "eth", "shortcut": "HAND", @@ -11414,7 +11434,7 @@ "links": { "Homepage": "https://heartbout.com" }, - "marketcap_usd": 5873, + "marketcap_usd": 11910, "name": "HeartBout", "network": "eth", "shortcut": "HB", @@ -11550,7 +11570,7 @@ "Github": "https://github.com/bitcoinHEX", "Homepage": "https://hex.win" }, - "marketcap_usd": 7511735555, + "marketcap_usd": 10933582176, "name": "HEX", "network": "eth", "shortcut": "HEX", @@ -11744,7 +11764,7 @@ "links": { "Homepage": "https://humaniq.com" }, - "marketcap_usd": 846268, + "marketcap_usd": 774910, "name": "Humaniq", "network": "eth", "shortcut": "HMQ", @@ -11842,7 +11862,7 @@ "Github": "https://github.com/Holo-Host", "Homepage": "https://holo.host/" }, - "marketcap_usd": 344440090, + "marketcap_usd": 357796049, "name": "Holo Token", "network": "eth", "shortcut": "HOT (Holo)", @@ -11861,7 +11881,7 @@ "links": { "Homepage": "https://thehydrofoundation.com/" }, - "marketcap_usd": 1212987, + "marketcap_usd": 2858970, "name": "Hydro Protocol", "network": "eth", "shortcut": "HOT (Hydro)", @@ -11899,7 +11919,7 @@ "links": { "Homepage": "https://www.hbg.com" }, - "marketcap_usd": 1270062242, + "marketcap_usd": 856172229, "name": "Huobi Token", "network": "eth", "shortcut": "HT", @@ -12018,7 +12038,7 @@ "Github": "https://github.com/HiveProjectLTD", "Homepage": "https://www.hiveterminal.com" }, - "marketcap_usd": 639056, + "marketcap_usd": 479398, "name": "Hiveterminal Token", "network": "eth", "shortcut": "HVN", @@ -12173,7 +12193,7 @@ "links": { "Homepage": "https://www.everest.org" }, - "marketcap_usd": 7623699, + "marketcap_usd": 4268877, "name": "Everest (ID)", "network": "eth", "shortcut": "ID", @@ -12231,7 +12251,7 @@ "Github": "https://github.com/rupiah-token/", "Homepage": "https://www.rupiahtoken.com" }, - "marketcap_usd": 7891601, + "marketcap_usd": 7924738, "name": "Rupiah Token", "network": "eth", "shortcut": "IDRT", @@ -12250,7 +12270,7 @@ "links": { "Homepage": "https://investfeed.com" }, - "marketcap_usd": 117198, + "marketcap_usd": 134220, "name": "InvestFeed", "network": "eth", "shortcut": "IFT", @@ -12269,7 +12289,7 @@ "links": { "Homepage": "http://igtoken.net" }, - "marketcap_usd": 66382, + "marketcap_usd": 69787, "name": "IGToken", "network": "eth", "shortcut": "IG", @@ -12308,7 +12328,7 @@ "links": { "Homepage": "https://ihtcoin.com" }, - "marketcap_usd": 209407, + "marketcap_usd": 159461, "name": "I HOUSE TOKEN", "network": "eth", "shortcut": "IHT", @@ -12385,7 +12405,7 @@ "links": { "Homepage": "https://indorse.io" }, - "marketcap_usd": 129641, + "marketcap_usd": 148825, "name": "Indorse", "network": "eth", "shortcut": "IND", @@ -12560,7 +12580,7 @@ "Github": "https://github.com/iotexproject/iotex-core", "Homepage": "http://iotex.io/" }, - "marketcap_usd": 265267970, + "marketcap_usd": 264394884, "name": "IoTeX Network", "network": "eth", "shortcut": "IOTX", @@ -12580,7 +12600,7 @@ "Github": "https://github.com/InsurePal", "Homepage": "https://insurepal.io/" }, - "marketcap_usd": 24064, + "marketcap_usd": 34625, "name": "InsurePal token", "network": "eth", "shortcut": "IPL", @@ -12619,7 +12639,7 @@ "Github": "https://github.com/iqeon", "Homepage": "https://iqeon.io/" }, - "marketcap_usd": 1548039, + "marketcap_usd": 1731566, "name": "IQeon", "network": "eth", "shortcut": "IQN", @@ -12679,7 +12699,7 @@ "Github": "https://github.com/IoTChainCode", "Homepage": "https://iotchain.io/" }, - "marketcap_usd": 10816, + "marketcap_usd": 0, "name": "IoT Chain", "network": "eth", "shortcut": "ITC", @@ -12777,7 +12797,7 @@ "links": { "Homepage": "https://www.insurex.co" }, - "marketcap_usd": 159977, + "marketcap_usd": 60603, "name": "InsureX", "network": "eth", "shortcut": "IXT", @@ -12836,7 +12856,7 @@ "Github": "https://github.com/TokyoToken/JasmyCoin", "Homepage": "https://jasmy.co.jp" }, - "marketcap_usd": 24741311, + "marketcap_usd": 285268503, "name": "JasmyCoin", "network": "eth", "shortcut": "JASMY", @@ -12933,7 +12953,7 @@ "Github": "https://github.com/JobchainOfficial", "Homepage": "https://www.jobchain.com" }, - "marketcap_usd": 6283133, + "marketcap_usd": 2519134, "name": "Jobchain", "network": "eth", "shortcut": "JOB", @@ -13010,7 +13030,7 @@ "links": { "Homepage": "http://www.kan.land" }, - "marketcap_usd": 10574643, + "marketcap_usd": 11935328, "name": "BitKan", "network": "eth", "shortcut": "KAN", @@ -13088,7 +13108,7 @@ "links": { "Homepage": "https://kindads.io" }, - "marketcap_usd": 10723, + "marketcap_usd": 0, "name": "Kind Ads Token", "network": "eth", "shortcut": "KIND", @@ -13146,7 +13166,7 @@ "links": { "Homepage": "https://kanadecoin.com" }, - "marketcap_usd": 84677, + "marketcap_usd": 89020, "name": "KanadeCoin", "network": "eth", "shortcut": "KNDC", @@ -13224,7 +13244,7 @@ "Github": "https://github.com/Cryptense/", "Homepage": "https://kryll.io/" }, - "marketcap_usd": 13931057, + "marketcap_usd": 11584452, "name": "Kryll", "network": "eth", "shortcut": "KRL", @@ -13379,7 +13399,7 @@ "Github": "https://github.com/latoken", "Homepage": "https://latoken.com/" }, - "marketcap_usd": 20871451, + "marketcap_usd": 12469110, "name": "LATOKEN", "network": "eth", "shortcut": "LA", @@ -13418,7 +13438,7 @@ "Github": "https://github.com/LambdaIM", "Homepage": "https://www.lambda.im/" }, - "marketcap_usd": 2524621, + "marketcap_usd": 3436594, "name": "Lambda", "network": "eth", "shortcut": "LAMB", @@ -13456,7 +13476,7 @@ "links": { "Homepage": "https://www.mycred.io" }, - "marketcap_usd": 1236883, + "marketcap_usd": 1984754, "name": "Cred", "network": "eth", "shortcut": "LBA", @@ -13475,7 +13495,7 @@ "links": { "Homepage": "https://www.localcoinswap.com" }, - "marketcap_usd": 378219, + "marketcap_usd": 0, "name": "LocalCoinSwap", "network": "eth", "shortcut": "LCS", @@ -13766,7 +13786,7 @@ "links": { "Homepage": "https://link.smartcontract.com" }, - "marketcap_usd": 3811788891, + "marketcap_usd": 3706867339, "name": "Chainlink", "network": "eth", "shortcut": "LINK (Chainlink)", @@ -13824,7 +13844,7 @@ "Github": "https://github.com/GNYIO", "Homepage": "https://www.gny.io/lisk" }, - "marketcap_usd": 268579, + "marketcap_usd": 513069, "name": "Lisk Machine Learning", "network": "eth", "shortcut": "LML", @@ -13922,7 +13942,7 @@ "links": { "Homepage": "https://www.locuschain.com" }, - "marketcap_usd": 62589574, + "marketcap_usd": 106084983, "name": "Locus Chain", "network": "eth", "shortcut": "LOCUS", @@ -13979,7 +13999,7 @@ "links": { "Homepage": "https://looksrare.org/" }, - "marketcap_usd": 98650767, + "marketcap_usd": 87358911, "name": "LooksRare", "network": "eth", "shortcut": "LOOKS", @@ -13999,7 +14019,7 @@ "Github": "github.com/loomnetwork/", "Homepage": "https://loomx.io" }, - "marketcap_usd": 62700878, + "marketcap_usd": 68665519, "name": "LOOM", "network": "eth", "shortcut": "LOOM", @@ -14019,7 +14039,7 @@ "Github": "https://github.com/livepeer", "Homepage": "https://livepeer.org/" }, - "marketcap_usd": 231874740, + "marketcap_usd": 202398732, "name": "Livepeer Token", "network": "eth", "shortcut": "LPT", @@ -14058,7 +14078,7 @@ "Github": "https://github.com/loopring", "Homepage": "https://loopring.org" }, - "marketcap_usd": 342544118, + "marketcap_usd": 488986992, "name": "Loopring", "network": "eth", "shortcut": "LRC", @@ -14156,7 +14176,7 @@ "Github": "https://github.com/lunyr", "Homepage": "https://lunyr.com" }, - "marketcap_usd": 49719, + "marketcap_usd": 39214, "name": "Lunyr", "network": "eth", "shortcut": "LUN", @@ -14254,7 +14274,7 @@ "Github": "https://github.com/TreasureProject", "Homepage": "https://treasure.lol/" }, - "marketcap_usd": 70602976, + "marketcap_usd": 334874623, "name": "MAGIC", "network": "eth", "shortcut": "MAGIC", @@ -14294,7 +14314,7 @@ "Github": "https://github.com/decentraland", "Homepage": "https://decentraland.org" }, - "marketcap_usd": 1169611173, + "marketcap_usd": 1197020889, "name": "Decentraland MANA", "network": "eth", "shortcut": "MANA", @@ -14332,7 +14352,7 @@ "links": { "Homepage": "https://midasprotocol.io/" }, - "marketcap_usd": 79376, + "marketcap_usd": 46340, "name": "MIDAS PROTOCOL", "network": "eth", "shortcut": "MAS", @@ -14351,7 +14371,7 @@ "links": { "Homepage": "https://polygon.technology/" }, - "marketcap_usd": 7431717654, + "marketcap_usd": 10673372779, "name": "Matic Token", "network": "eth", "shortcut": "MATIC", @@ -14448,7 +14468,7 @@ "links": { "Homepage": "https://moedaseeds.com" }, - "marketcap_usd": 919826, + "marketcap_usd": 1291844, "name": "Moeda Loyalty Points", "network": "eth", "shortcut": "MDA", @@ -14467,7 +14487,7 @@ "links": { "Homepage": "https://www.mdt.co" }, - "marketcap_usd": 20390256, + "marketcap_usd": 41400337, "name": "Measurable Data Token", "network": "eth", "shortcut": "MDT", @@ -14543,7 +14563,7 @@ "links": { "Homepage": "https://dontbuymeme.com" }, - "marketcap_usd": 2272818, + "marketcap_usd": 1541109, "name": "Meme", "network": "eth", "shortcut": "MEME", @@ -14597,25 +14617,6 @@ } ] }, - "erc20:eth:MET": { - "address": "0xa3d58c4E56fedCae3a7c43A725aeE9A71F0ece4e", - "links": { - "Homepage": "https://www.metronome.io" - }, - "marketcap_usd": 0, - "name": "Metronome", - "network": "eth", - "shortcut": "MET", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": [ - { - "name": "Trezor Suite", - "url": "https://suite.trezor.io" - } - ] - }, "erc20:eth:METM": { "address": "0xFEF3884b603C33EF8eD4183346E093A173C94da6", "links": { @@ -14641,7 +14642,7 @@ "Github": "https://github.com/syncfab", "Homepage": "https://syncfab.com/" }, - "marketcap_usd": 2566024, + "marketcap_usd": 1569704, "name": "SyncFab Smart Manufacturing Blockchain", "network": "eth", "shortcut": "MFG", @@ -14661,7 +14662,7 @@ "Github": "https://github.com/MainframeHQ", "Homepage": "https://mainframe.com" }, - "marketcap_usd": 50822421, + "marketcap_usd": 46148389, "name": "Mainframe Token", "network": "eth", "shortcut": "MFT", @@ -14795,7 +14796,7 @@ "links": { "Homepage": "https://token.morpheuslabs.io" }, - "marketcap_usd": 2690049, + "marketcap_usd": 9699325, "name": "Morpheus Infrastructure Token", "network": "eth", "shortcut": "MITX", @@ -14815,7 +14816,7 @@ "Github": "https://github.com/makerdao", "Homepage": "https://makerdao.com" }, - "marketcap_usd": 833169733, + "marketcap_usd": 754815596, "name": "MakerDAO", "network": "eth", "shortcut": "MKR", @@ -14854,7 +14855,7 @@ "links": { "Homepage": "https://melonport.com" }, - "marketcap_usd": 47193145, + "marketcap_usd": 51522892, "name": "Melonport", "network": "eth", "shortcut": "MLN (new)", @@ -14951,7 +14952,7 @@ "Github": "https://github.com/Goldmint", "Homepage": "https://goldmint.io" }, - "marketcap_usd": 193104, + "marketcap_usd": 187104, "name": "Goldmint MNT Prelaunch Token", "network": "eth", "shortcut": "MNTP", @@ -15144,7 +15145,7 @@ "Github": "https://github.com/mstable", "Homepage": "http://mstable.org" }, - "marketcap_usd": 4359472, + "marketcap_usd": 1711726, "name": "mStable Meta", "network": "eth", "shortcut": "MTA", @@ -15163,7 +15164,7 @@ "links": { "Homepage": "http://www.monetha.io" }, - "marketcap_usd": 4681353, + "marketcap_usd": 3108167, "name": "Monetha", "network": "eth", "shortcut": "MTH", @@ -15182,7 +15183,7 @@ "links": { "Homepage": "https://www.metalpay.com" }, - "marketcap_usd": 65144606, + "marketcap_usd": 86278153, "name": "Metal", "network": "eth", "shortcut": "MTL", @@ -15201,7 +15202,7 @@ "links": { "Homepage": "https://medicalchain.com" }, - "marketcap_usd": 670512, + "marketcap_usd": 569197, "name": "MedToken", "network": "eth", "shortcut": "MTN", @@ -15335,7 +15336,7 @@ "links": { "Homepage": "http://mvlchain.io" }, - "marketcap_usd": 96022968, + "marketcap_usd": 103186956, "name": "Mass Vehicle Ledger Token", "network": "eth", "shortcut": "MVL", @@ -15355,7 +15356,7 @@ "Github": "https://github.com/Merculet", "Homepage": "https://www.merculet.io" }, - "marketcap_usd": 496252, + "marketcap_usd": 364084, "name": "Merculet", "network": "eth", "shortcut": "MVP", @@ -15393,7 +15394,7 @@ "links": { "Homepage": "https://mysterium.network/" }, - "marketcap_usd": 5246057, + "marketcap_usd": 5220095, "name": "Mysterium", "network": "eth", "shortcut": "MYST", @@ -15432,7 +15433,7 @@ "Github": "https://github.com/NANJ-COIN", "Homepage": "https://nanjcoin.com/" }, - "marketcap_usd": 299669, + "marketcap_usd": 315039, "name": "NANJCOIN", "network": "eth", "shortcut": "NANJ", @@ -15529,7 +15530,7 @@ "links": { "Homepage": "https://niobiumcoin.io" }, - "marketcap_usd": 109065, + "marketcap_usd": 89666, "name": "Niobium Coin", "network": "eth", "shortcut": "NBC", @@ -15588,7 +15589,7 @@ "Github": "https://github.com/polyswarm", "Homepage": "https://polyswarm.io" }, - "marketcap_usd": 13700176, + "marketcap_usd": 16924718, "name": "Nectar", "network": "eth", "shortcut": "NCT", @@ -15724,7 +15725,7 @@ "links": { "Homepage": "http://nexo.io" }, - "marketcap_usd": 553351854, + "marketcap_usd": 392895017, "name": "Nexo", "network": "eth", "shortcut": "NEXO", @@ -15802,7 +15803,7 @@ "Github": "https://github.com/nknorg", "Homepage": "https://nkn.org" }, - "marketcap_usd": 62094180, + "marketcap_usd": 90576077, "name": "NKN", "network": "eth", "shortcut": "NKN", @@ -15842,7 +15843,7 @@ "Github": "https://github.com/numerai", "Homepage": "https://numer.ai" }, - "marketcap_usd": 86844258, + "marketcap_usd": 116826641, "name": "Numerai", "network": "eth", "shortcut": "NMR", @@ -15960,7 +15961,7 @@ "Github": "https://github.com/pundix", "Homepage": "https://pundix.com" }, - "marketcap_usd": 125687612, + "marketcap_usd": 133949059, "name": "Pundi X Token", "network": "eth", "shortcut": "NPXS", @@ -16019,7 +16020,7 @@ "Github": "https://github.com/nucypher", "Homepage": "https://nucypher.com" }, - "marketcap_usd": 101175117, + "marketcap_usd": 106813292, "name": "NuCypher Network", "network": "eth", "shortcut": "NU", @@ -16192,7 +16193,7 @@ "links": { "Homepage": "https://www.openanx.org/en" }, - "marketcap_usd": 11045486, + "marketcap_usd": 18826379, "name": "OAX", "network": "eth", "shortcut": "OAX", @@ -16252,7 +16253,7 @@ "Github": "https://github.com/oceanprotocol", "Homepage": "https://oceanprotocol.com" }, - "marketcap_usd": 102841179, + "marketcap_usd": 241205777, "name": "Ocean Token", "network": "eth", "shortcut": "OCEAN", @@ -16271,7 +16272,7 @@ "links": { "Homepage": "http://www.ocnex.net" }, - "marketcap_usd": 673175, + "marketcap_usd": 453298, "name": "Odyssey", "network": "eth", "shortcut": "OCN", @@ -16291,7 +16292,7 @@ "Github": "https://github.com/octofi", "Homepage": "https://octo.fi" }, - "marketcap_usd": 1288408, + "marketcap_usd": 1060203, "name": "OctoFi", "network": "eth", "shortcut": "OCTO", @@ -16351,7 +16352,7 @@ "Github": "https://github.com/originprotocol", "Homepage": "https://www.originprotocol.com" }, - "marketcap_usd": 71147679, + "marketcap_usd": 69749547, "name": "OriginToken", "network": "eth", "shortcut": "OGN", @@ -16371,7 +16372,7 @@ "Github": "https://github.com/OriginProtocol/ousd-governance", "Homepage": "https://governance.ousd.com" }, - "marketcap_usd": 5775820, + "marketcap_usd": 4568560, "name": "Origin Dollar Governance", "network": "eth", "shortcut": "OGV", @@ -16431,7 +16432,7 @@ "Github": "https://github.com/okex/okberc20token", "Homepage": "https://www.okex.com/" }, - "marketcap_usd": 969890641, + "marketcap_usd": 3080708053, "name": "OKB", "network": "eth", "shortcut": "OKB", @@ -16510,7 +16511,7 @@ "Github": "https://github.com/omisego", "Homepage": "https://omg.omise.co" }, - "marketcap_usd": 223158726, + "marketcap_usd": 243064600, "name": "OmiseGO", "network": "eth", "shortcut": "OMG", @@ -16588,7 +16589,7 @@ "Github": "https://github.com/onGsocial", "Homepage": "https://somee.social" }, - "marketcap_usd": 156806, + "marketcap_usd": 0, "name": "SoMee.Social", "network": "eth", "shortcut": "ONG", @@ -16723,7 +16724,7 @@ "Github": "https://github.com/orbs-network", "Homepage": "https://orbs.com" }, - "marketcap_usd": 93149464, + "marketcap_usd": 102029850, "name": "Orbs", "network": "eth", "shortcut": "ORBS", @@ -16801,7 +16802,7 @@ "links": { "Homepage": "https://www.originsport.io" }, - "marketcap_usd": 2666847, + "marketcap_usd": 867139, "name": "Origin Sport", "network": "eth", "shortcut": "ORS", @@ -16860,7 +16861,7 @@ "Github": "https://github.com/OpenSTFoundation", "Homepage": "https://simpletoken.org" }, - "marketcap_usd": 370635, + "marketcap_usd": 406211, "name": "Simple Token 'OST'", "network": "eth", "shortcut": "OST", @@ -16919,7 +16920,7 @@ "Github": "https://github.com/originprotocol", "Homepage": "https://ousd.com" }, - "marketcap_usd": 44949191, + "marketcap_usd": 54479420, "name": "Origin Dollar", "network": "eth", "shortcut": "OUSD", @@ -16979,7 +16980,7 @@ "Github": "https://github.com/orchidtechnologies/orchid", "Homepage": "https://www.orchid.com" }, - "marketcap_usd": 63182725, + "marketcap_usd": 65851872, "name": "Orchid", "network": "eth", "shortcut": "OXT", @@ -17170,33 +17171,13 @@ } ] }, - "erc20:eth:PAX": { - "address": "0x8E870D67F660D95d5be530380D0eC0bd388289E1", - "links": { - "Github": "https://github.com/paxosglobal", - "Homepage": "https://www.paxos.com/standard" - }, - "marketcap_usd": 942628878, - "name": "Paxos Standard (PAX)", - "network": "eth", - "shortcut": "PAX", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": [ - { - "name": "Trezor Suite", - "url": "https://suite.trezor.io" - } - ] - }, "erc20:eth:PAXG": { "address": "0x45804880De22913dAFE09f4980848ECE6EcbAf78", "links": { "Github": "https://github.com/paxosglobal/paxos-gold-contract", "Homepage": "https://www.paxos.com/paxgold" }, - "marketcap_usd": 536030375, + "marketcap_usd": 487662967, "name": "Paxos Gold", "network": "eth", "shortcut": "PAXG", @@ -17215,7 +17196,7 @@ "links": { "Homepage": "http://www.tenx.tech" }, - "marketcap_usd": 3590819, + "marketcap_usd": 3568855, "name": "TenX", "network": "eth", "shortcut": "PAY", @@ -17468,7 +17449,7 @@ "links": { "Homepage": "https://www.phitoken.io" }, - "marketcap_usd": 346234, + "marketcap_usd": 0, "name": "PHI Token", "network": "eth", "shortcut": "PHI", @@ -17487,7 +17468,7 @@ "links": { "Homepage": "https://pickle.finance/" }, - "marketcap_usd": 2562812, + "marketcap_usd": 2689140, "name": "Pickle Finance", "network": "eth", "shortcut": "PICKLE", @@ -17506,7 +17487,7 @@ "links": { "Homepage": "https://piplcoin.com" }, - "marketcap_usd": 70438, + "marketcap_usd": 0, "name": "PIPL Coin", "network": "eth", "shortcut": "PIPL", @@ -17582,7 +17563,7 @@ "links": { "Homepage": "http://pkgtoken.io" }, - "marketcap_usd": 75503, + "marketcap_usd": 79184, "name": "PKG Token", "network": "eth", "shortcut": "PKG", @@ -17601,7 +17582,7 @@ "links": { "Homepage": "https://playkey.io" }, - "marketcap_usd": 70182, + "marketcap_usd": 65171, "name": "Playkey", "network": "eth", "shortcut": "PKT", @@ -17659,7 +17640,7 @@ "Github": "https://github.com/twentythirty/PillarToken", "Homepage": "https://www.pillarproject.io" }, - "marketcap_usd": 1324382, + "marketcap_usd": 1387534, "name": "Pillar Project", "network": "eth", "shortcut": "PLR", @@ -17698,7 +17679,7 @@ "links": { "Homepage": "https://plutus.it" }, - "marketcap_usd": 17063353, + "marketcap_usd": 18172960, "name": "Pluton", "network": "eth", "shortcut": "PLU", @@ -17717,7 +17698,7 @@ "links": { "Homepage": "https://pumapay.io" }, - "marketcap_usd": 661088, + "marketcap_usd": 420840, "name": "PumaPay", "network": "eth", "shortcut": "PMA", @@ -17756,7 +17737,7 @@ "Github": "https://github.com/kleros", "Homepage": "https://kleros.io" }, - "marketcap_usd": 18696127, + "marketcap_usd": 18057635, "name": "Pinakion", "network": "eth", "shortcut": "PNK", @@ -17775,7 +17756,7 @@ "links": { "Homepage": "https://po.et" }, - "marketcap_usd": 57715, + "marketcap_usd": 66098, "name": "Po.et Tokens", "network": "eth", "shortcut": "POE", @@ -17832,7 +17813,7 @@ "links": { "Homepage": "https://polymath.network" }, - "marketcap_usd": 240203817, + "marketcap_usd": 178335520, "name": "Polymath Network", "network": "eth", "shortcut": "POLY", @@ -17910,7 +17891,7 @@ "links": { "Homepage": "https://powerledger.io" }, - "marketcap_usd": 96355268, + "marketcap_usd": 104709098, "name": "PowerLedger", "network": "eth", "shortcut": "POWR", @@ -17949,7 +17930,7 @@ "Github": "https://github.com/Bitpopulous", "Homepage": "https://populous.co" }, - "marketcap_usd": 3475930, + "marketcap_usd": 4787929, "name": "Populous", "network": "eth", "shortcut": "PPT", @@ -18007,7 +17988,7 @@ "links": { "Homepage": "https://privatix.io" }, - "marketcap_usd": 36757, + "marketcap_usd": 45253, "name": "Privatix", "network": "eth", "shortcut": "PRIX", @@ -18066,7 +18047,7 @@ "Github": "https://github.com/propsproject", "Homepage": "https://propsproject.com" }, - "marketcap_usd": 537419, + "marketcap_usd": 824153, "name": "Props", "network": "eth", "shortcut": "PROPS", @@ -18163,7 +18144,7 @@ "links": { "Homepage": "https://primas.io" }, - "marketcap_usd": 504019, + "marketcap_usd": 659916, "name": "Primas", "network": "eth", "shortcut": "PST", @@ -18222,7 +18203,7 @@ "links": { "Homepage": "https://patientory.com" }, - "marketcap_usd": 204432, + "marketcap_usd": 483007, "name": "Patientory", "network": "eth", "shortcut": "PTOY", @@ -18341,7 +18322,7 @@ "Github": "https://github.com/playgame-global", "Homepage": "https://its.playgame.com" }, - "marketcap_usd": 229958, + "marketcap_usd": 200007, "name": "PlayGame", "network": "eth", "shortcut": "PXG", @@ -18418,7 +18399,7 @@ "links": { "Homepage": "https://liquid.plus" }, - "marketcap_usd": 8040437, + "marketcap_usd": 9192048, "name": "QASH", "network": "eth", "shortcut": "QASH", @@ -18496,7 +18477,7 @@ "links": { "Homepage": "https://quarkchain.io" }, - "marketcap_usd": 70288774, + "marketcap_usd": 74074939, "name": "QuarkChain", "network": "eth", "shortcut": "QKC", @@ -18516,7 +18497,7 @@ "Github": "https://github.com/quantnetwork", "Homepage": "https://www.quant.network/" }, - "marketcap_usd": 1937265880, + "marketcap_usd": 1530947563, "name": "Quant", "network": "eth", "shortcut": "QNT", @@ -18575,7 +18556,7 @@ "Github": "https://github.com/quantstamp", "Homepage": "https://quantstamp.com/" }, - "marketcap_usd": 11074696, + "marketcap_usd": 11601312, "name": "Quantstamp Token", "network": "eth", "shortcut": "QSP", @@ -18673,7 +18654,7 @@ "Github": "https://github.com/rokfin/eth-contracts", "Homepage": "https://www.raetoken.org" }, - "marketcap_usd": 7105329, + "marketcap_usd": 7985545, "name": "RAE Token", "network": "eth", "shortcut": "RAE", @@ -18693,7 +18674,7 @@ "Github": "https://github.com/reflexer-labs/", "Homepage": "https://reflexer.finance/" }, - "marketcap_usd": 13330827, + "marketcap_usd": 6622639, "name": "Rai Reflex Index", "network": "eth", "shortcut": "RAI", @@ -18732,7 +18713,7 @@ "links": { "Homepage": "http://token.dprating.com" }, - "marketcap_usd": 314949, + "marketcap_usd": 344016, "name": "DPRating", "network": "eth", "shortcut": "RATING", @@ -18752,7 +18733,7 @@ "Github": "https://github.com/rublixdev", "Homepage": "https://rublix.io/" }, - "marketcap_usd": 488481, + "marketcap_usd": 535673, "name": "Rublix", "network": "eth", "shortcut": "RBLX", @@ -18791,7 +18772,7 @@ "Github": "https://github.com/ripio/rcn-token", "Homepage": "https://ripiocredit.network" }, - "marketcap_usd": 2163152, + "marketcap_usd": 1108878, "name": "Ripio Credit Network", "network": "eth", "shortcut": "RCN", @@ -18811,7 +18792,7 @@ "Github": "https://github.com/raiden-network/raiden/", "Homepage": "https://raiden.network" }, - "marketcap_usd": 3580682, + "marketcap_usd": 2880243, "name": "Raiden Network", "network": "eth", "shortcut": "RDN", @@ -18908,7 +18889,7 @@ "Github": "https://github.com/red", "Homepage": "https://ico.red-lang.org" }, - "marketcap_usd": 294012, + "marketcap_usd": 293525, "name": "Red Community Token", "network": "eth", "shortcut": "RED", @@ -18948,7 +18929,7 @@ "Github": "http://github.com/reef-defi", "Homepage": "http://reef.finance" }, - "marketcap_usd": 103902135, + "marketcap_usd": 70777959, "name": "Reef Finance", "network": "eth", "shortcut": "REEF", @@ -18986,7 +18967,7 @@ "links": { "Homepage": "https://remme.io" }, - "marketcap_usd": 229320, + "marketcap_usd": 152526, "name": "Remme", "network": "eth", "shortcut": "REM", @@ -19026,7 +19007,7 @@ "Github": "https://github.com/renproject", "Homepage": "https://renproject.io/" }, - "marketcap_usd": 121689922, + "marketcap_usd": 97699774, "name": "Republic Token", "network": "eth", "shortcut": "REN", @@ -19045,7 +19026,7 @@ "links": { "Homepage": "https://augur.net" }, - "marketcap_usd": 75174722, + "marketcap_usd": 100020697, "name": "Augur", "network": "eth", "shortcut": "REP", @@ -19083,7 +19064,7 @@ "links": { "Homepage": "https://request.network" }, - "marketcap_usd": 109511441, + "marketcap_usd": 113609078, "name": "Request Network", "network": "eth", "shortcut": "REQ", @@ -19103,7 +19084,7 @@ "Github": "https://github.com/Revain", "Homepage": "https://revain.org" }, - "marketcap_usd": 65306287, + "marketcap_usd": 50618632, "name": "Revain", "network": "eth", "shortcut": "REV", @@ -19142,7 +19123,7 @@ "links": { "Homepage": "https://refereum.com" }, - "marketcap_usd": 29801018, + "marketcap_usd": 33221299, "name": "Refereum", "network": "eth", "shortcut": "RFR", @@ -19219,7 +19200,7 @@ "links": { "Homepage": "http://iex.ec/" }, - "marketcap_usd": 91098475, + "marketcap_usd": 154000623, "name": "IEx.ec", "network": "eth", "shortcut": "RLC", @@ -19335,7 +19316,7 @@ "links": { "Homepage": "https://www.oneroot.io/en" }, - "marketcap_usd": 466138, + "marketcap_usd": 532562, "name": "OneRoot Network", "network": "eth", "shortcut": "RNT", @@ -19450,7 +19431,7 @@ "links": { "Homepage": "https://roobee.io/" }, - "marketcap_usd": 3249133, + "marketcap_usd": 4479943, "name": "ROOBEE", "network": "eth", "shortcut": "ROOBEE", @@ -19509,7 +19490,7 @@ "Github": "https://github.com/reserve-protocol/rsr-mainnet", "Homepage": "https://reserve.org" }, - "marketcap_usd": 269834814, + "marketcap_usd": 234795022, "name": "Reserve Rights", "network": "eth", "shortcut": "RSR", @@ -19567,7 +19548,7 @@ "links": { "Homepage": "https://www.rotharium.io" }, - "marketcap_usd": 2684480, + "marketcap_usd": 3022419, "name": "Rotharium", "network": "eth", "shortcut": "RTH", @@ -19606,7 +19587,7 @@ "links": { "Homepage": "http://ruffchain.com" }, - "marketcap_usd": 469488, + "marketcap_usd": 349118, "name": "Ruff", "network": "eth", "shortcut": "RUFF", @@ -19664,7 +19645,7 @@ "links": { "Homepage": "https://rivetzintl.com" }, - "marketcap_usd": 15264, + "marketcap_usd": 15562, "name": "Rivetz", "network": "eth", "shortcut": "RVT", @@ -19780,7 +19761,7 @@ "links": { "Homepage": "https://saltlending.com" }, - "marketcap_usd": 3777096, + "marketcap_usd": 6351019, "name": "Salt Lending Token", "network": "eth", "shortcut": "SALT", @@ -19799,7 +19780,7 @@ "links": { "Homepage": "https://santiment.net" }, - "marketcap_usd": 5247616, + "marketcap_usd": 3377746, "name": "Santiment", "network": "eth", "shortcut": "SAN", @@ -19837,7 +19818,7 @@ "links": { "Homepage": "https://ico.nexus.social" }, - "marketcap_usd": 40941, + "marketcap_usd": 0, "name": "SocialCoin", "network": "eth", "shortcut": "SCL", @@ -19914,7 +19895,7 @@ "links": { "Homepage": "https://www.sentinel-chain.org" }, - "marketcap_usd": 88114, + "marketcap_usd": 90109, "name": "Sentinel Chain", "network": "eth", "shortcut": "SENC", @@ -20126,7 +20107,7 @@ "links": { "Homepage": "https://shibatoken.com" }, - "marketcap_usd": 6622933610, + "marketcap_usd": 6796478899, "name": "SHIBA INU", "network": "eth", "shortcut": "SHIB", @@ -20300,7 +20281,7 @@ "links": { "Homepage": "https://www.skb-coin.jp/en" }, - "marketcap_usd": 303604, + "marketcap_usd": 364958, "name": "Sakura Bloom", "network": "eth", "shortcut": "SKB", @@ -20339,7 +20320,7 @@ "Github": "https://github.com/Steamtradenet/smart-contract", "Homepage": "https://skincoin.org" }, - "marketcap_usd": 42310, + "marketcap_usd": 53089, "name": "SKIN", "network": "eth", "shortcut": "SKIN", @@ -20435,7 +20416,7 @@ "links": { "Homepage": "https://youengine.io" }, - "marketcap_usd": 137424432, + "marketcap_usd": 137126516, "name": "Smooth Love Potion", "network": "eth", "shortcut": "SLP", @@ -20512,7 +20493,7 @@ "links": { "Homepage": "https://suncontract.org" }, - "marketcap_usd": 3104164, + "marketcap_usd": 3613310, "name": "SunContract", "network": "eth", "shortcut": "SNC", @@ -20648,7 +20629,7 @@ "Github": "https://github.com/status-im", "Homepage": "https://status.im" }, - "marketcap_usd": 96694800, + "marketcap_usd": 117873087, "name": "Status Network Token", "network": "eth", "shortcut": "SNT", @@ -20662,45 +20643,6 @@ } ] }, - "erc20:eth:SNTR": { - "address": "0x2859021eE7F2Cb10162E67F33Af2D22764B31aFf", - "links": { - "Homepage": "https://silentnotary.com" - }, - "marketcap_usd": 0, - "name": "Silent Notary", - "network": "eth", - "shortcut": "SNTR", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": [ - { - "name": "Trezor Suite", - "url": "https://suite.trezor.io" - } - ] - }, - "erc20:eth:SNX:c011": { - "address": "0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F", - "links": { - "Github": "https://github.com/havven/havven", - "Homepage": "https://synthetix.io" - }, - "marketcap_usd": 734410291, - "name": "Synthetix Network Token", - "network": "eth", - "shortcut": "SNX", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": [ - { - "name": "Trezor Suite", - "url": "https://suite.trezor.io" - } - ] - }, "erc20:eth:SOAR": { "address": "0xD65960FAcb8E4a2dFcb2C2212cb2e44a02e2a57E", "links": { @@ -20725,7 +20667,7 @@ "links": { "Homepage": "https://www.allsportschain.com" }, - "marketcap_usd": 2504853, + "marketcap_usd": 2272034, "name": "All Sports", "network": "eth", "shortcut": "SOC", @@ -20783,7 +20725,7 @@ "Github": "https://github.com/cryptosoulgame", "Homepage": "https://cryptosoul.io/" }, - "marketcap_usd": 158451, + "marketcap_usd": 125890, "name": "CryptoSoul", "network": "eth", "shortcut": "SOUL", @@ -20919,7 +20861,7 @@ "links": { "Homepage": "https://spindle.zone" }, - "marketcap_usd": 87705, + "marketcap_usd": 23855, "name": "SPINDLE", "network": "eth", "shortcut": "SPD", @@ -21016,7 +20958,7 @@ "Github": "https://github.com/sirin-labs/crowdsale-smart-contract", "Homepage": "https://sirinlabs.com" }, - "marketcap_usd": 1207199, + "marketcap_usd": 916240, "name": "Sirin Labs", "network": "eth", "shortcut": "SRN", @@ -21050,31 +20992,12 @@ } ] }, - "erc20:eth:SSH": { - "address": "0x6e2050CBFB3eD8A4d39b64cC9f47E711a03a5a89", - "links": { - "Homepage": "https://www.stream.space" - }, - "marketcap_usd": 0, - "name": "StreamShares", - "network": "eth", - "shortcut": "SSH", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": [ - { - "name": "Trezor Suite", - "url": "https://suite.trezor.io" - } - ] - }, "erc20:eth:SSP": { "address": "0x624d520BAB2E4aD83935Fa503fB130614374E850", "links": { "Homepage": "https://smartshare.vip/#" }, - "marketcap_usd": 64955, + "marketcap_usd": 150024, "name": "Smartshare", "network": "eth", "shortcut": "SSP", @@ -21112,7 +21035,7 @@ "links": { "Homepage": "https://coinstarter.com" }, - "marketcap_usd": 9972, + "marketcap_usd": 9313, "name": "Starter Coin", "network": "eth", "shortcut": "STAC", @@ -21150,7 +21073,7 @@ "links": { "Homepage": "http://starbase.co" }, - "marketcap_usd": 141366, + "marketcap_usd": 37318, "name": "Star Token", "network": "eth", "shortcut": "STAR", @@ -21306,7 +21229,7 @@ "Github": "https://github.com/Storj", "Homepage": "https://storj.io" }, - "marketcap_usd": 188212148, + "marketcap_usd": 178806759, "name": "STORJ", "network": "eth", "shortcut": "STORJ", @@ -21384,7 +21307,7 @@ "links": { "Homepage": "https://staker.network" }, - "marketcap_usd": 346, + "marketcap_usd": 0, "name": "Staker", "network": "eth", "shortcut": "STR", @@ -21443,7 +21366,7 @@ "Github": "https://github.com/stx-technologies/stox-token", "Homepage": "https://www.stox.com" }, - "marketcap_usd": 192514, + "marketcap_usd": 184023, "name": "StoxToken", "network": "eth", "shortcut": "STX", @@ -21463,7 +21386,7 @@ "Github": "https://github.com/SubstratumNetwork", "Homepage": "https://substratum.net" }, - "marketcap_usd": 254151, + "marketcap_usd": 186040, "name": "Substratum", "network": "eth", "shortcut": "SUB", @@ -21502,7 +21425,7 @@ "Github": "https://github.com/sushiswap", "Homepage": "https://sushiswapclassic.org/" }, - "marketcap_usd": 192919186, + "marketcap_usd": 288234769, "name": "SushiToken", "network": "eth", "shortcut": "SUSHI", @@ -21561,7 +21484,7 @@ "Github": "https://github.com/swashapp/", "Homepage": "https://swashapp.io/" }, - "marketcap_usd": 6562959, + "marketcap_usd": 12397242, "name": "Swash Token", "network": "eth", "shortcut": "SWASH", @@ -21580,7 +21503,7 @@ "links": { "Homepage": "http://www.swftcoin.com" }, - "marketcap_usd": 5678376, + "marketcap_usd": 5259537, "name": "SwftCoin", "network": "eth", "shortcut": "SWFTC", @@ -21614,12 +21537,31 @@ } ] }, + "erc20:eth:SWRM": { + "address": "0x6e2050CBFB3eD8A4d39b64cC9f47E711a03a5a89", + "links": { + "Homepage": "https://www.stream.space" + }, + "marketcap_usd": 0, + "name": "StreamShares", + "network": "eth", + "shortcut": "SWRM", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "erc20", + "wallet": [ + { + "name": "Trezor Suite", + "url": "https://suite.trezor.io" + } + ] + }, "erc20:eth:SWT": { "address": "0xB9e7F8568e08d5659f5D29C4997173d84CdF2607", "links": { "Homepage": "http://swarm.city" }, - "marketcap_usd": 331278, + "marketcap_usd": 264688, "name": "Swarm City Token", "network": "eth", "shortcut": "SWT", @@ -21677,7 +21619,7 @@ "links": { "Homepage": "http://www.spectre.ai" }, - "marketcap_usd": 35050, + "marketcap_usd": 0, "name": "Spectre.ai U-Token", "network": "eth", "shortcut": "SXUT", @@ -21768,6 +21710,26 @@ } ] }, + "erc20:eth:Skey": { + "address": "0x06A01a4d579479Dd5D884EBf61A31727A3d8D442", + "links": { + "Github": "https://github.com/skey-network", + "Homepage": "https://skey.network" + }, + "marketcap_usd": 3338194, + "name": "Skey Network", + "network": "eth", + "shortcut": "Skey", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "erc20", + "wallet": [ + { + "name": "Trezor Suite", + "url": "https://suite.trezor.io" + } + ] + }, "erc20:eth:TALAO": { "address": "0x1D4cCC31dAB6EA20f461d329a0562C1c58412515", "links": { @@ -21792,7 +21754,7 @@ "links": { "Homepage": "https://taklimakan.io" }, - "marketcap_usd": 19916, + "marketcap_usd": 0, "name": "Taklimakan Network", "network": "eth", "shortcut": "TAN", @@ -21950,7 +21912,7 @@ "links": { "Homepage": "https://tokenbox.io" }, - "marketcap_usd": 20127, + "marketcap_usd": 14975, "name": "Tokenbox", "network": "eth", "shortcut": "TBX", @@ -22009,7 +21971,7 @@ "links": { "Homepage": "https://www.thorecash.com" }, - "marketcap_usd": 7323, + "marketcap_usd": 5891, "name": "Thore Cash", "network": "eth", "shortcut": "TCH", @@ -22100,6 +22062,25 @@ } ] }, + "erc20:eth:TDX": { + "address": "0x317eb4ad9cfaC6232f0046831322E895507bcBeb", + "links": { + "Homepage": "https://tidex.com/" + }, + "marketcap_usd": 0, + "name": "Tidex Token", + "network": "eth", + "shortcut": "TDX", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "erc20", + "wallet": [ + { + "name": "Trezor Suite", + "url": "https://suite.trezor.io" + } + ] + }, "erc20:eth:TEAM": { "address": "0x1c79ab32C66aCAa1e9E81952B8AAa581B43e54E7", "links": { @@ -22143,7 +22124,7 @@ "links": { "Homepage": "https://www.tokenomy.com" }, - "marketcap_usd": 4610212, + "marketcap_usd": 5058206, "name": "Tokenomy", "network": "eth", "shortcut": "TEN", @@ -22376,7 +22357,7 @@ "links": { "Homepage": "https://chronobase.eu/" }, - "marketcap_usd": 153209, + "marketcap_usd": 145674, "name": "ChronoBase", "network": "eth", "shortcut": "TIK", @@ -22433,7 +22414,7 @@ "links": { "Homepage": "https://www.blocktix.io" }, - "marketcap_usd": 8165, + "marketcap_usd": 0, "name": "Blocktix", "network": "eth", "shortcut": "TIX", @@ -22490,7 +22471,7 @@ "links": { "Homepage": "https://etherscan.io/token/TokenCard" }, - "marketcap_usd": 2450113, + "marketcap_usd": 2583432, "name": "TokenCard", "network": "eth", "shortcut": "TKN", @@ -22510,7 +22491,7 @@ "Github": "https://github.com/Tokpie/tokpie-contract", "Homepage": "https://tokpie.io/" }, - "marketcap_usd": 5478008, + "marketcap_usd": 4434791, "name": "TOKPIE", "network": "eth", "shortcut": "TKP", @@ -22607,7 +22588,7 @@ "links": { "Homepage": "https://transcodium.com" }, - "marketcap_usd": 31401, + "marketcap_usd": 28525, "name": "Transcodium", "network": "eth", "shortcut": "TNS", @@ -22685,7 +22666,7 @@ "links": { "Homepage": "https://origintrail.io" }, - "marketcap_usd": 83002002, + "marketcap_usd": 134439035, "name": "OriginTrail", "network": "eth", "shortcut": "TRAC", @@ -22781,7 +22762,7 @@ "Github": "https://github.com/WeTrustPlatform", "Homepage": "https://www.wetrust.io" }, - "marketcap_usd": 180864, + "marketcap_usd": 184461, "name": "WeTrust", "network": "eth", "shortcut": "TRST", @@ -22795,6 +22776,25 @@ } ] }, + "erc20:eth:TRUCCO": { + "address": "0xf10E3E8F6B02B594b7C95fca59DC7E5ce7364df5", + "links": { + "Homepage": "https://trucco.com" + }, + "marketcap_usd": 0, + "name": "Truco Token", + "network": "eth", + "shortcut": "TRUCCO", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "erc20", + "wallet": [ + { + "name": "Trezor Suite", + "url": "https://suite.trezor.io" + } + ] + }, "erc20:eth:TSW": { "address": "0x6B87999bE87358065bBdE41e8a0fe0B7b1cd2514", "links": { @@ -22898,7 +22898,7 @@ "Github": "https://github.com/trusttoken", "Homepage": "https://www.trusttoken.com" }, - "marketcap_usd": 820554921, + "marketcap_usd": 1116979619, "name": "TrueUSD", "network": "eth", "shortcut": "TUSD", @@ -22995,7 +22995,7 @@ "Github": "https://github.com/ubex-ai", "Homepage": "https://www.ubex.com/" }, - "marketcap_usd": 274997, + "marketcap_usd": 125184, "name": "UBEX Token", "network": "eth", "shortcut": "UBEX", @@ -23014,7 +23014,7 @@ "links": { "Homepage": "https://unibright.io" }, - "marketcap_usd": 32157926, + "marketcap_usd": 22769484, "name": "Unibright", "network": "eth", "shortcut": "UBT", @@ -23091,7 +23091,7 @@ "links": { "Homepage": "https://www.upfiring.com" }, - "marketcap_usd": 764252, + "marketcap_usd": 415184, "name": "Upfiring", "network": "eth", "shortcut": "UFR", @@ -23131,7 +23131,7 @@ "Github": "https://github.com/umbrella-network", "Homepage": "https://umb.network/" }, - "marketcap_usd": 790885, + "marketcap_usd": 1566544, "name": "Umbrella", "network": "eth", "shortcut": "UMB", @@ -23150,7 +23150,7 @@ "links": { "Homepage": "https://uniswap.org/" }, - "marketcap_usd": 5269763951, + "marketcap_usd": 4976163328, "name": "Uniswap", "network": "eth", "shortcut": "UNI", @@ -23169,7 +23169,7 @@ "links": { "Homepage": "https://uptoken.org" }, - "marketcap_usd": 39773, + "marketcap_usd": 46974, "name": "UpToken", "network": "eth", "shortcut": "UP", @@ -23188,7 +23188,7 @@ "links": { "Homepage": "https://sentinelprotocol.io" }, - "marketcap_usd": 27540163, + "marketcap_usd": 34499041, "name": "Sentinel Protocol", "network": "eth", "shortcut": "UPP", @@ -23287,7 +23287,7 @@ "Github": "https://github.com/centrehq/centre-tokens", "Homepage": "https://www.centre.io" }, - "marketcap_usd": 43023962517, + "marketcap_usd": 42518789234, "name": "USD//Coin", "network": "eth", "shortcut": "USDC", @@ -23301,12 +23301,32 @@ } ] }, + "erc20:eth:USDP": { + "address": "0x8E870D67F660D95d5be530380D0eC0bd388289E1", + "links": { + "Github": "https://github.com/paxosglobal", + "Homepage": "https://www.paxos.com/standard" + }, + "marketcap_usd": 877457873, + "name": "Paxos Standard (PAX)", + "network": "eth", + "shortcut": "USDP", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "erc20", + "wallet": [ + { + "name": "Trezor Suite", + "url": "https://suite.trezor.io" + } + ] + }, "erc20:eth:USDS": { "address": "0xA4Bdb11dc0a2bEC88d24A3aa1E6Bb17201112eBe", "links": { "Homepage": "https://stably.io" }, - "marketcap_usd": 462950, + "marketcap_usd": 462366, "name": "StableUSD", "network": "eth", "shortcut": "USDS", @@ -23325,7 +23345,7 @@ "links": { "Homepage": "https://tether.to" }, - "marketcap_usd": 69415999585, + "marketcap_usd": 70941600691, "name": "USD Tether (erc20)", "network": "eth", "shortcut": "USDT", @@ -23365,7 +23385,7 @@ "Github": "https://github.com/utrustdev/", "Homepage": "https://utrust.com" }, - "marketcap_usd": 71785543, + "marketcap_usd": 58070830, "name": "Utrust", "network": "eth", "shortcut": "UTK", @@ -23423,7 +23443,7 @@ "links": { "Homepage": "https://u.network/" }, - "marketcap_usd": 302316, + "marketcap_usd": 268098, "name": "U Networks", "network": "eth", "shortcut": "UUU", @@ -23443,7 +23463,7 @@ "Github": "https://github.com/smartvalor/ValorToken", "Homepage": "https://smartvalor.com" }, - "marketcap_usd": 4175274, + "marketcap_usd": 4484748, "name": "ValorToken", "network": "eth", "shortcut": "VALOR", @@ -23483,7 +23503,7 @@ "Github": "https://github.com/VeriDocGlobal", "Homepage": "https://www.veridocglobal.com/" }, - "marketcap_usd": 2253205, + "marketcap_usd": 1861173, "name": "VeriDocGlobal", "network": "eth", "shortcut": "VDG", @@ -23523,7 +23543,7 @@ "Github": "https://github.com/blockv", "Homepage": "https://blockv.io" }, - "marketcap_usd": 10800084, + "marketcap_usd": 9193229, "name": "BLOCKv", "network": "eth", "shortcut": "VEE", @@ -23543,7 +23563,7 @@ "Github": "https://github.com/vegaprotocol", "Homepage": "https://vega.xyz" }, - "marketcap_usd": 40734485, + "marketcap_usd": 57183218, "name": "Vega", "network": "eth", "shortcut": "VEGA", @@ -23600,7 +23620,7 @@ "links": { "Homepage": "https://veritas.veritaseum.com" }, - "marketcap_usd": 114662928, + "marketcap_usd": 67691562, "name": "Veritaseum", "network": "eth", "shortcut": "VERI", @@ -23619,7 +23639,7 @@ "links": { "Homepage": "https://www.viberate.com" }, - "marketcap_usd": 13703693, + "marketcap_usd": 21389600, "name": "Viberate", "network": "eth", "shortcut": "VIB", @@ -23638,7 +23658,7 @@ "links": { "Homepage": "https://www.vibehub.io" }, - "marketcap_usd": 636043, + "marketcap_usd": 448626, "name": "VIBE Coin", "network": "eth", "shortcut": "VIBE", @@ -23678,7 +23698,7 @@ "Github": "https://github.com/videocoin", "Homepage": "https://www.videocoin.io" }, - "marketcap_usd": 7151414, + "marketcap_usd": 4612359, "name": "VideoCoin", "network": "eth", "shortcut": "VID", @@ -23737,7 +23757,7 @@ "links": { "Homepage": "https://ico.vikky.io" }, - "marketcap_usd": 57685, + "marketcap_usd": 60643, "name": "VikkyToken", "network": "eth", "shortcut": "VIKKY", @@ -23951,7 +23971,7 @@ "links": { "Homepage": "https://victoriavr.com/" }, - "marketcap_usd": 8622254, + "marketcap_usd": 11153234, "name": "Victoria VR", "network": "eth", "shortcut": "VR", @@ -24086,7 +24106,7 @@ "links": { "Homepage": "https://wab.network" }, - "marketcap_usd": 263029, + "marketcap_usd": 276519, "name": "WABnetwork", "network": "eth", "shortcut": "WAB", @@ -24105,7 +24125,7 @@ "links": { "Homepage": "https://taelpay.com" }, - "marketcap_usd": 10719972, + "marketcap_usd": 13590697, "name": "Tael", "network": "eth", "shortcut": "WABI", @@ -24224,7 +24244,7 @@ "Github": "https://github.com/WrappedBTC", "Homepage": "https://wbtc.network" }, - "marketcap_usd": 5005923961, + "marketcap_usd": 3841396768, "name": "Wrapped Bitcoin", "network": "eth", "shortcut": "WBTC", @@ -24297,6 +24317,26 @@ } ] }, + "erc20:eth:WDOGE": { + "address": "0x8aa9381b2544b48c26f3b850F6e07E2c5161EB3e", + "links": { + "Github": "https://github.com/WrappedBTC/bitcoin-token-smart-contracts/blob/master/ethereumV2/contracts/token/WDOGE.sol", + "Homepage": "https://wdoge.tech/" + }, + "marketcap_usd": 0, + "name": "Wrapped DOGE", + "network": "eth", + "shortcut": "WDOGE", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "erc20", + "wallet": [ + { + "name": "Trezor Suite", + "url": "https://suite.trezor.io" + } + ] + }, "erc20:eth:WEB": { "address": "0x840fe75ABfaDc0F2d54037829571B2782e919ce4", "links": { @@ -24419,7 +24459,7 @@ "links": { "Homepage": "https://wings.ai" }, - "marketcap_usd": 438057, + "marketcap_usd": 176076, "name": "WINGS", "network": "eth", "shortcut": "WINGS", @@ -24595,7 +24635,7 @@ "links": { "Homepage": "https://wepower.network" }, - "marketcap_usd": 499436, + "marketcap_usd": 166674, "name": "WePower Token", "network": "eth", "shortcut": "WPR", @@ -24614,7 +24654,7 @@ "links": { "Homepage": "https://worldcore.eu" }, - "marketcap_usd": 27825, + "marketcap_usd": 59023, "name": "Worldcore", "network": "eth", "shortcut": "WRC", @@ -24733,7 +24773,7 @@ "links": { "Homepage": "https://x8currency.com" }, - "marketcap_usd": 607929, + "marketcap_usd": 529029, "name": "X8X", "network": "eth", "shortcut": "X8X", @@ -24771,7 +24811,7 @@ "links": { "Homepage": "http://www.xaurum.org" }, - "marketcap_usd": 1482163, + "marketcap_usd": 1880493, "name": "Xaurum", "network": "eth", "shortcut": "XAUR", @@ -24810,7 +24850,7 @@ "Github": "https://github.com/blitzpredict", "Homepage": "https://www.blitzpredict.io" }, - "marketcap_usd": 54445, + "marketcap_usd": 58228, "name": "BlitzPredict", "network": "eth", "shortcut": "XBP", @@ -24849,7 +24889,7 @@ "links": { "Homepage": "https://www.swisscryptotokens.ch/" }, - "marketcap_usd": 3067642, + "marketcap_usd": 3302072, "name": "CryptoFranc", "network": "eth", "shortcut": "XCHF", @@ -24902,6 +24942,26 @@ } ] }, + "erc20:eth:XDATA": { + "address": "0x0Cf0Ee63788A0849fE5297F3407f701E122cC023", + "links": { + "Github": "https://github.com/streamr-dev", + "Homepage": "https://www.streamr.com" + }, + "marketcap_usd": 0, + "name": "Streamr DATAcoin", + "network": "eth", + "shortcut": "XDATA", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "erc20", + "wallet": [ + { + "name": "Trezor Suite", + "url": "https://suite.trezor.io" + } + ] + }, "erc20:eth:XDCE": { "address": "0x41AB1b6fcbB2fA9DCEd81aCbdeC13Ea6315F2Bf2", "links": { @@ -25081,7 +25141,7 @@ "links": { "Homepage": "https://xio.network/" }, - "marketcap_usd": 460358, + "marketcap_usd": 244530, "name": "XIO Network", "network": "eth", "shortcut": "XIO", @@ -25140,7 +25200,7 @@ "Github": "https://github.com/XMaxPlatform", "Homepage": "https://www.xmx.com" }, - "marketcap_usd": 586053, + "marketcap_usd": 304015, "name": "XMax", "network": "eth", "shortcut": "XMX", @@ -25217,7 +25277,7 @@ "links": { "Homepage": "http://www.xov.io" }, - "marketcap_usd": 4387, + "marketcap_usd": 4613, "name": "XOVBank", "network": "eth", "shortcut": "XOV", @@ -25236,7 +25296,7 @@ "links": { "Homepage": "https://xpa.io" }, - "marketcap_usd": 30973, + "marketcap_usd": 32562, "name": "XPA", "network": "eth", "shortcut": "XPA", @@ -25296,7 +25356,7 @@ "Github": "https://github.com/ProtonProtocol", "Homepage": "https://www.protonchain.com/" }, - "marketcap_usd": 32249238, + "marketcap_usd": 24851023, "name": "Proton", "network": "eth", "shortcut": "XPR", @@ -25315,7 +25375,7 @@ "links": { "Homepage": "https://cryptobuyer.io" }, - "marketcap_usd": 12921, + "marketcap_usd": 0, "name": "Cryptobuyer Token", "network": "eth", "shortcut": "XPT", @@ -25373,7 +25433,7 @@ "Github": "https://github.com/Xfers/StraitsX-tokens", "Homepage": "https://xfers.com/sg/stablecoin" }, - "marketcap_usd": 62481970, + "marketcap_usd": 59730862, "name": "Singapore-Dollar Backed Stablecoin", "network": "eth", "shortcut": "XSGD", @@ -25392,7 +25452,7 @@ "links": { "Homepage": "https://xyo.network" }, - "marketcap_usd": 75464119, + "marketcap_usd": 72604094, "name": "XYO", "network": "eth", "shortcut": "XYO", @@ -25451,7 +25511,7 @@ "links": { "Homepage": "http://www.yeefoundation.com" }, - "marketcap_usd": 120132, + "marketcap_usd": 244413, "name": "Yee Token", "network": "eth", "shortcut": "YEE", @@ -25471,7 +25531,7 @@ "Github": "https://github.com/iearn-finance", "Homepage": "https://yearn.finance/" }, - "marketcap_usd": 288446827, + "marketcap_usd": 341339987, "name": "yearn.finance", "network": "eth", "shortcut": "YFI", @@ -25491,7 +25551,7 @@ "Github": "https://github.com/yfii/vault", "Homepage": "https://dfi.money/" }, - "marketcap_usd": 49157075, + "marketcap_usd": 50523406, "name": "YFII.finance", "network": "eth", "shortcut": "YFII", @@ -25571,7 +25631,7 @@ "Github": "https://github.com/zapproject", "Homepage": "https://zap.store" }, - "marketcap_usd": 818230, + "marketcap_usd": 842523, "name": "ZAP", "network": "eth", "shortcut": "ZAP", @@ -25590,7 +25650,7 @@ "links": { "Homepage": "https://0chain.net" }, - "marketcap_usd": 8387189, + "marketcap_usd": 10060329, "name": "0chain", "network": "eth", "shortcut": "ZCN", @@ -25628,7 +25688,7 @@ "links": { "Homepage": "https://zsc.io/" }, - "marketcap_usd": 133916, + "marketcap_usd": 93153, "name": "Zeusshield", "network": "eth", "shortcut": "ZCS", @@ -25706,7 +25766,7 @@ "Github": "https://github.com/ZEUS-coin", "Homepage": "https://zeusfundme.com/" }, - "marketcap_usd": 14288, + "marketcap_usd": 0, "name": "ZeusNetwork", "network": "eth", "shortcut": "ZEUS", @@ -25725,7 +25785,7 @@ "links": { "Homepage": "https://zinc.work" }, - "marketcap_usd": 4915, + "marketcap_usd": 5020, "name": "ZINC", "network": "eth", "shortcut": "ZINC", @@ -25860,7 +25920,7 @@ "links": { "Homepage": "https://zper.io" }, - "marketcap_usd": 21616, + "marketcap_usd": 0, "name": "ZPER", "network": "eth", "shortcut": "ZPR", @@ -25880,7 +25940,7 @@ "Github": "https://github.com/0xProject", "Homepage": "https://0xproject.com" }, - "marketcap_usd": 214088667, + "marketcap_usd": 222664444, "name": "0x Project", "network": "eth", "shortcut": "ZRX", @@ -26996,7 +27056,7 @@ "Github": "https://github.com/eosdac", "Homepage": "https://eosdac.io/" }, - "marketcap_usd": 310246, + "marketcap_usd": 295500, "name": "eosDAC", "network": "eth", "shortcut": "eosDAC", @@ -27050,6 +27110,26 @@ } ] }, + "erc20:eth:iBBT": { + "address": "0x691c25C461DaFC47792b6E4d674FBB637bca1C6F", + "links": { + "Github": "https://github.com/Geopay/iBlockchain-Bank-and-Trust-Utility-Token", + "Homepage": "https://ibbt.io" + }, + "marketcap_usd": 0, + "name": "iBBT Utility Token", + "network": "eth", + "shortcut": "iBBT", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "erc20", + "wallet": [ + { + "name": "Trezor Suite", + "url": "https://suite.trezor.io" + } + ] + }, "erc20:eth:iKNC": { "address": "0x1cC9567EA2eB740824a45F8026cCF8e46973234D", "links": { @@ -33199,8 +33279,12 @@ "type": "erc20", "wallet": [ { - "name": "Trezor Suite", - "url": "https://suite.trezor.io" + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" } ] }, @@ -33218,12 +33302,16 @@ "type": "erc20", "wallet": [ { - "name": "Trezor Suite", - "url": "https://suite.trezor.io" + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" } ] }, - "erc20:rsk:BPro": { + "erc20:rsk:BITP": { "address": "0x440CD83C160De5C96Ddb20246815eA44C7aBBCa8", "links": { "Github": "https://github.com/money-on-chain/", @@ -33232,7 +33320,7 @@ "marketcap_usd": 0, "name": "BitPro", "network": "rsk", - "shortcut": "BPro", + "shortcut": "BITP", "t1_enabled": "yes", "t2_enabled": "yes", "type": "erc20", @@ -33551,6 +33639,27 @@ } ] }, + "eth:$BNI": { + "links": { + "Homepage": "https://bitindi.org" + }, + "marketcap_usd": 0, + "name": "Bitindi", + "shortcut": "$BNI", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:$OC": { "links": { "Homepage": "https://digitalnext.business/SocialSmartChain" @@ -33572,13 +33681,13 @@ } ] }, - "eth:0xF": { + "eth:0XT": { "links": { - "Homepage": "https://freighttrust.com" + "Homepage": "https://www.0xtrade.finance/" }, "marketcap_usd": 0, - "name": "Freight Trust Network", - "shortcut": "0xF", + "name": "0XTade", + "shortcut": "0XT", "t1_enabled": "yes", "t2_enabled": "yes", "type": "coin", @@ -33593,13 +33702,13 @@ } ] }, - "eth:420": { + "eth:0xF": { "links": { - "Homepage": "https://420integrated.com" + "Homepage": "https://freighttrust.com" }, "marketcap_usd": 0, - "name": "420coin", - "shortcut": "420", + "name": "Freight Trust Network", + "shortcut": "0xF", "t1_enabled": "yes", "t2_enabled": "yes", "type": "coin", @@ -33656,11 +33765,32 @@ } ] }, + "eth:ACE": { + "links": { + "Homepage": "https://ace.fusionist.io/" + }, + "marketcap_usd": 0, + "name": "Endurance Smart Chain", + "shortcut": "ACE", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:AIOZ": { "links": { "Homepage": "https://aioz.network" }, - "marketcap_usd": 32129157, + "marketcap_usd": 33770520, "name": "AIOZ Network", "shortcut": "AIOZ", "t1_enabled": "yes", @@ -33702,7 +33832,7 @@ "links": { "Homepage": "https://akroma.io" }, - "marketcap_usd": 15658, + "marketcap_usd": 22416, "name": "Akroma", "shortcut": "AKA", "t1_enabled": "yes", @@ -33719,6 +33849,132 @@ } ] }, + "eth:ALOT": { + "links": { + "Homepage": "https://dexalot.com" + }, + "marketcap_usd": 0, + "name": "Dexalot Subnet", + "shortcut": "ALOT", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:ALPH": { + "links": { + "Homepage": "https://alph.network" + }, + "marketcap_usd": 0, + "name": "Alph Network", + "shortcut": "ALPH", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:ALT": { + "links": { + "Homepage": "https://altcoinchain.org" + }, + "marketcap_usd": 0, + "name": "Altcoinchain", + "shortcut": "ALT", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:ALYX": { + "links": { + "Homepage": "https://www.alyxchain.com" + }, + "marketcap_usd": 0, + "name": "Alyx", + "shortcut": "ALYX", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:AMAX": { + "links": { + "Homepage": "https://amax.network" + }, + "marketcap_usd": 0, + "name": "Armonia Eva Chain", + "shortcut": "AMAX", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:AMB": { + "links": { + "Homepage": "https://airdao.io" + }, + "marketcap_usd": 27921475, + "name": "AirDAO", + "shortcut": "AMB", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:AMBROS": { "links": { "Homepage": "https://ambros.network" @@ -33761,6 +34017,27 @@ } ] }, + "eth:ANY": { + "links": { + "Homepage": "https://evm.anytype.io" + }, + "marketcap_usd": 0, + "name": "Anytype EVM Chain", + "shortcut": "ANY", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:APTA": { "links": { "Homepage": "https://bloqs4good.com" @@ -33803,6 +34080,48 @@ } ] }, + "eth:AR\u00c9V": { + "links": { + "Homepage": "" + }, + "marketcap_usd": 0, + "name": "Arevia", + "shortcut": "AR\u00c9V", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:ASA": { + "links": { + "Homepage": "https://astranaut.io" + }, + "marketcap_usd": 0, + "name": "Astra", + "shortcut": "ASA", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:ASK": { "links": { "Homepage": "https://permission.io/" @@ -33828,7 +34147,7 @@ "links": { "Homepage": "https://astar.network/" }, - "marketcap_usd": 149516327, + "marketcap_usd": 328182166, "name": "Astar", "shortcut": "ASTR", "t1_enabled": "yes", @@ -33845,6 +34164,27 @@ } ] }, + "eth:ATLR": { + "links": { + "Homepage": "https://1971.network/" + }, + "marketcap_usd": 0, + "name": "Atelier", + "shortcut": "ATLR", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:ATS": { "links": { "Homepage": "https://artis.eco" @@ -33905,6 +34245,27 @@ } ] }, + "eth:AVS": { + "links": { + "Homepage": "https://avescoin.io" + }, + "marketcap_usd": 0, + "name": "Aves", + "shortcut": "AVS", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:BCS": { "links": { "Homepage": "https://blockchainstation.io" @@ -33947,11 +34308,95 @@ } ] }, + "eth:BFC": { + "links": { + "Homepage": "https://thebifrost.io" + }, + "marketcap_usd": 94426967, + "name": "Bifrost", + "shortcut": "BFC", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:BG": { + "links": { + "Homepage": "https://beagle.chat/" + }, + "marketcap_usd": 0, + "name": "Beagle Messaging Chain", + "shortcut": "BG", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:BIT": { + "links": { + "Homepage": "https://mantle.xyz" + }, + "marketcap_usd": 0, + "name": "Mantle", + "shortcut": "BIT", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:BITCI": { + "links": { + "Homepage": "https://www.bitcichain.com" + }, + "marketcap_usd": 0, + "name": "Bitcichain", + "shortcut": "BITCI", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:BNB": { "links": { "Homepage": "https://www.binance.org" }, - "marketcap_usd": 51094687147, + "marketcap_usd": 47729156144, "name": "Binance Smart Chain", "shortcut": "BNB", "t1_enabled": "yes", @@ -33968,13 +34413,34 @@ } ] }, - "eth:BOBA": { + "eth:BOA": { "links": { - "Homepage": "https://boba.network" + "Homepage": "https://docs.bosagora.org" }, - "marketcap_usd": 0, - "name": "Boba Network Bobabase", - "shortcut": "BOBA", + "marketcap_usd": 10622097, + "name": "BOSagora", + "shortcut": "BOA", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:BOMB": { + "links": { + "Homepage": "https://www.bombchain.com" + }, + "marketcap_usd": 108635, + "name": "BOMB Chain", + "shortcut": "BOMB", "t1_enabled": "yes", "t2_enabled": "yes", "type": "coin", @@ -34031,13 +34497,13 @@ } ] }, - "eth:BRO": { + "eth:BRNKC": { "links": { - "Homepage": "https://brochain.org" + "Homepage": "https://bearnetwork.net" }, "marketcap_usd": 0, - "name": "BROChain", - "shortcut": "BRO", + "name": "Bear Network Chain", + "shortcut": "BRNKC", "t1_enabled": "yes", "t2_enabled": "yes", "type": "coin", @@ -34115,6 +34581,27 @@ } ] }, + "eth:BTON": { + "links": { + "Homepage": "https://blocktoncoin.com" + }, + "marketcap_usd": 0, + "name": "Blockton Blockchain", + "shortcut": "BTON", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:BTT": { "links": { "Homepage": "https://bittorrentchain.io/" @@ -34136,6 +34623,48 @@ } ] }, + "eth:BTY": { + "links": { + "Homepage": "https://www.bityuan.com" + }, + "marketcap_usd": 0, + "name": "BitYuan", + "shortcut": "BTY", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:BXN": { + "links": { + "Homepage": "https://blackfort.exchange" + }, + "marketcap_usd": 0, + "name": "BlackFort Exchange Network", + "shortcut": "BXN", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:Brise": { "links": { "Homepage": "https://bitgert.com/" @@ -34157,6 +34686,48 @@ } ] }, + "eth:CAM:500": { + "links": { + "Homepage": "https://camino.foundation/" + }, + "marketcap_usd": 0, + "name": "Camino C-Chain", + "shortcut": "CAM", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:CANTO": { + "links": { + "Homepage": "https://canto.io" + }, + "marketcap_usd": 0, + "name": "Canto", + "shortcut": "CANTO", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:CATE": { "links": { "Homepage": "https://catechain.com" @@ -34178,6 +34749,27 @@ } ] }, + "eth:CCNA": { + "links": { + "Homepage": "https://oortech.com" + }, + "marketcap_usd": 0, + "name": "Oort Ascraeus", + "shortcut": "CCNA", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:CCP": { "links": { "Homepage": "https://www.cryptocoinpay.co" @@ -34203,7 +34795,7 @@ "links": { "Homepage": "https://docs.celo.org/" }, - "marketcap_usd": 333164130, + "marketcap_usd": 370021788, "name": "Celo", "shortcut": "CELO", "t1_enabled": "yes", @@ -34262,6 +34854,27 @@ } ] }, + "eth:CIC": { + "links": { + "Homepage": "https://www.cicchain.net" + }, + "marketcap_usd": 0, + "name": "CIC Chain", + "shortcut": "CIC", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:CLASS": { "links": { "Homepage": "https://velaverse.io" @@ -34283,12 +34896,33 @@ } ] }, + "eth:CLD": { + "links": { + "Homepage": "https://cloudtx.finance" + }, + "marketcap_usd": 0, + "name": "CloudTx", + "shortcut": "CLD", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:CLO": { "coinmarketcap_alias": "callisto-network", "links": { "Homepage": "https://callisto.network" }, - "marketcap_usd": 23364762, + "marketcap_usd": 9476443, "name": "Callisto", "shortcut": "CLO", "t1_enabled": "yes", @@ -34326,6 +34960,48 @@ } ] }, + "eth:CMEMO": { + "links": { + "Homepage": "www.memolabs.org" + }, + "marketcap_usd": 0, + "name": "Memo Smart Chain", + "shortcut": "CMEMO", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:CMP": { + "links": { + "Homepage": "https://caduceus.foundation/" + }, + "marketcap_usd": 0, + "name": "CMP-Mainnet", + "shortcut": "CMP", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:CNDL": { "links": { "Homepage": "https://candlelabs.org/" @@ -34347,6 +35023,48 @@ } ] }, + "eth:CONDOR": { + "links": { + "Homepage": "https://condor.systems" + }, + "marketcap_usd": 0, + "name": "Condor Test Network", + "shortcut": "CONDOR", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:CORE": { + "links": { + "Homepage": "https://www.coredao.org" + }, + "marketcap_usd": 0, + "name": "Core Blockchain", + "shortcut": "CORE", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:CPAY:21337": { "links": { "Homepage": "https://cennz.net" @@ -34431,11 +35149,53 @@ } ] }, + "eth:CRC": { + "links": { + "Homepage": "https://github.com/ethereum-pocr/pocrnet" + }, + "marketcap_usd": 0, + "name": "PoCRNet", + "shortcut": "CRC", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:CREDIT": { + "links": { + "Homepage": "https://creditsmartchain.com" + }, + "marketcap_usd": 238095, + "name": "Credit Smartchain", + "shortcut": "CREDIT", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:CRO": { "links": { "Homepage": "https://cronos.org/" }, - "marketcap_usd": 2722455558, + "marketcap_usd": 2011736262, "name": "Cronos", "shortcut": "CRO", "t1_enabled": "yes", @@ -34473,6 +35233,27 @@ } ] }, + "eth:CTEX": { + "links": { + "Homepage": "https://ctextoken.io" + }, + "marketcap_usd": 0, + "name": "Ctex Scan Blockchain", + "shortcut": "CTEX", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:CUBE": { "links": { "Homepage": "https://www.cube.network" @@ -34536,6 +35317,48 @@ } ] }, + "eth:DEB": { + "links": { + "Homepage": "https://anduschain.io/" + }, + "marketcap_usd": 0, + "name": "Anduschain", + "shortcut": "DEB", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:DEL": { + "links": { + "Homepage": "https://decimalchain.com" + }, + "marketcap_usd": 0, + "name": "Decimal Smart Chain", + "shortcut": "DEL", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:DEV": { "links": { "Homepage": "https://docs.moonbeam.network/networks/testnet/" @@ -34557,13 +35380,13 @@ } ] }, - "eth:DGCC": { + "eth:DFI": { "links": { - "Homepage": "https://digestgroup.ltd" + "Homepage": "https://meta.defichain.com/" }, "marketcap_usd": 0, - "name": "Digest Swarm Chain", - "shortcut": "DGCC", + "name": "DeFiChain EVM Network", + "shortcut": "DFI", "t1_enabled": "yes", "t2_enabled": "yes", "type": "coin", @@ -34599,13 +35422,97 @@ } ] }, - "eth:DTH": { + "eth:DKN": { "links": { - "Homepage": "https://dithereum.org" + "Homepage": "https://doken.dev/" }, "marketcap_usd": 0, - "name": "Dithereum", - "shortcut": "DTH", + "name": "DoKEN Super Chain", + "shortcut": "DKN", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:DOGS": { + "links": { + "Homepage": "https://dogcoin.network" + }, + "marketcap_usd": 0, + "name": "Dogcoin", + "shortcut": "DOGS", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:DOINX": { + "links": { + "Homepage": "" + }, + "marketcap_usd": 0, + "name": "D-Chain", + "shortcut": "DOINX", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:DOS": { + "links": { + "Homepage": "http://doschain.io/" + }, + "marketcap_usd": 0, + "name": "Dos Fuji Subnet", + "shortcut": "DOS", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:DRAC": { + "links": { + "Homepage": "https://drac.io/" + }, + "marketcap_usd": 0, + "name": "DRAC Network", + "shortcut": "DRAC", "t1_enabled": "yes", "t2_enabled": "yes", "type": "coin", @@ -34662,6 +35569,27 @@ } ] }, + "eth:DXT": { + "links": { + "Homepage": "https://dexit.network" + }, + "marketcap_usd": 0, + "name": "Dexit Network", + "shortcut": "DXT", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:DYNO": { "links": { "Homepage": "https://dynoprotocol.com" @@ -34683,6 +35611,48 @@ } ] }, + "eth:Deh": { + "links": { + "Homepage": "https://dehvo.com" + }, + "marketcap_usd": 0, + "name": "Dehvo", + "shortcut": "Deh", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:ECG": { + "links": { + "Homepage": "https://ssquad.games/" + }, + "marketcap_usd": 0, + "name": "SPS", + "shortcut": "ECG", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:ECO": { "links": { "Homepage": "https://ecoball.org" @@ -34729,7 +35699,7 @@ "links": { "Homepage": "http://edgewa.re" }, - "marketcap_usd": 4543987, + "marketcap_usd": 4689635, "name": "Edgeware", "shortcut": "EDG", "t1_enabled": "yes", @@ -34746,11 +35716,32 @@ } ] }, + "eth:EGAZ": { + "links": { + "Homepage": "https://eticaprotocol.org" + }, + "marketcap_usd": 0, + "name": "Etica", + "shortcut": "EGAZ", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:EGEM": { "links": { "Homepage": "https://egem.io" }, - "marketcap_usd": 88557, + "marketcap_usd": 36379, "name": "EtherGem", "shortcut": "EGEM", "t1_enabled": "yes", @@ -34788,6 +35779,27 @@ } ] }, + "eth:EKTA": { + "links": { + "Homepage": "https://www.ekta.io" + }, + "marketcap_usd": 239141, + "name": "Ekta", + "shortcut": "EKTA", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:ELA:20": { "links": { "Homepage": "https://www.elastos.org/" @@ -34830,6 +35842,27 @@ } ] }, + "eth:EMPIRE": { + "links": { + "Homepage": "https://www.empirenetwork.io/" + }, + "marketcap_usd": 0, + "name": "Empire Network", + "shortcut": "EMPIRE", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:ENTER": { "links": { "Homepage": "https://entercoin.net" @@ -34898,7 +35931,7 @@ "links": { "Homepage": "https://ethereumclassic.org" }, - "marketcap_usd": 3265834039, + "marketcap_usd": 2950948570, "name": "Ethereum Classic", "shortcut": "ETC", "t1_enabled": "yes", @@ -34915,7 +35948,7 @@ "links": { "Homepage": "https://ethereum.org" }, - "marketcap_usd": 189401744793, + "marketcap_usd": 199115648941, "name": "Ethereum", "shortcut": "ETH", "t1_enabled": "yes", @@ -34928,11 +35961,32 @@ } ] }, + "eth:ETHF": { + "links": { + "Homepage": "https://etherfair.org" + }, + "marketcap_usd": 0, + "name": "ethereum Fair", + "shortcut": "ETHF", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:ETHO": { "links": { "Homepage": "https://ethoprotocol.com" }, - "marketcap_usd": 0, + "marketcap_usd": 910129, "name": "Etho Protocol", "shortcut": "ETHO", "t1_enabled": "yes", @@ -34991,6 +36045,27 @@ } ] }, + "eth:ETMP": { + "links": { + "Homepage": "https://etm.network" + }, + "marketcap_usd": 0, + "name": "Ennothem", + "shortcut": "ETMP", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:ETND": { "links": { "Homepage": "https://www.etnd.pro" @@ -35117,6 +36192,27 @@ } ] }, + "eth:EXL": { + "links": { + "Homepage": "" + }, + "marketcap_usd": 0, + "name": "Excoincial Chain", + "shortcut": "EXL", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:EXP": { "links": { "Homepage": "https://expanse.tech" @@ -35180,6 +36276,27 @@ } ] }, + "eth:FIL": { + "links": { + "Homepage": "https://filecoin.io" + }, + "marketcap_usd": 0, + "name": "Filecoin -", + "shortcut": "FIL", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:FIN": { "links": { "Homepage": "https://primusmoney.com" @@ -35201,11 +36318,74 @@ } ] }, + "eth:FIRE:529": { + "links": { + "Homepage": "https://thefirechain.com" + }, + "marketcap_usd": 0, + "name": "Firechain", + "shortcut": "FIRE", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:FITFI": { + "links": { + "Homepage": "https://step.network" + }, + "marketcap_usd": 0, + "name": "Step Network", + "shortcut": "FITFI", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:FLA": { + "links": { + "Homepage": "https://www.flaexchange.top" + }, + "marketcap_usd": 0, + "name": "Flachain", + "shortcut": "FLA", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:FLR": { "links": { "Homepage": "https://flare.xyz" }, - "marketcap_usd": 0, + "marketcap_usd": 467857625, "name": "Flare", "shortcut": "FLR", "t1_enabled": "yes", @@ -35222,13 +36402,34 @@ } ] }, - "eth:FRA": { + "eth:FNCY": { "links": { - "Homepage": "https://findora.org/" + "Homepage": "https://fncyscan.fncy.world" }, - "marketcap_usd": 14045778, - "name": "Findora", - "shortcut": "FRA", + "marketcap_usd": 38806503, + "name": "FNCY", + "shortcut": "FNCY", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:FREN": { + "links": { + "Homepage": "https://frenchain.app" + }, + "marketcap_usd": 0, + "name": "Frenchain", + "shortcut": "FREN", "t1_enabled": "yes", "t2_enabled": "yes", "type": "coin", @@ -35245,9 +36446,9 @@ }, "eth:FSN": { "links": { - "Homepage": "https://www.fusion.org/" + "Homepage": "https://fusion.org" }, - "marketcap_usd": 20067184, + "marketcap_usd": 26079174, "name": "Fusion", "shortcut": "FSN", "t1_enabled": "yes", @@ -35264,6 +36465,27 @@ } ] }, + "eth:FST": { + "links": { + "Homepage": "https://fantasia.technology/" + }, + "marketcap_usd": 0, + "name": "Fantasia Chain", + "shortcut": "FST", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:FTM": { "links": { "Homepage": "https://fantom.foundation" @@ -35394,7 +36616,7 @@ "links": { "Homepage": "https://moonbeam.network/networks/moonbeam/" }, - "marketcap_usd": 203962898, + "marketcap_usd": 255322438, "name": "Moonbeam", "shortcut": "GLMR", "t1_enabled": "yes", @@ -35411,6 +36633,48 @@ } ] }, + "eth:GLQ": { + "links": { + "Homepage": "https://graphlinq.io" + }, + "marketcap_usd": 0, + "name": "Graphlinq Blockchain", + "shortcut": "GLQ", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:GMMT": { + "links": { + "Homepage": "https://gmmtchain.io/" + }, + "marketcap_usd": 0, + "name": "Giant Mammoth", + "shortcut": "GMMT", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:GNC": { "links": { "Homepage": "https://genesis-gn.com" @@ -35436,7 +36700,7 @@ "links": { "Homepage": "https://gochain.io" }, - "marketcap_usd": 8860762, + "marketcap_usd": 9893895, "name": "GoChain", "shortcut": "GO", "t1_enabled": "yes", @@ -35453,13 +36717,34 @@ } ] }, - "eth:GT": { + "eth:GOLDT": { "links": { - "Homepage": "https://www.gatechain.io" + "Homepage": "https://www.hammerchain.io" }, "marketcap_usd": 0, - "name": "GateChain", - "shortcut": "GT", + "name": "Hammer Chain", + "shortcut": "GOLDT", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:GZN": { + "links": { + "Homepage": "https://token.gearzero.ca/mainnet" + }, + "marketcap_usd": 0, + "name": "Gear Zero Network", + "shortcut": "GZN", "t1_enabled": "yes", "t2_enabled": "yes", "type": "coin", @@ -35516,6 +36801,48 @@ } ] }, + "eth:HBAR:295": { + "links": { + "Homepage": "https://hedera.com" + }, + "marketcap_usd": 1876574858, + "name": "Hedera", + "shortcut": "HBAR", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:HMND": { + "links": { + "Homepage": "https://humanode.io" + }, + "marketcap_usd": 0, + "name": "Humanode", + "shortcut": "HMND", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:HO": { "links": { "Homepage": "https://halo.land/#/" @@ -35583,7 +36910,7 @@ "links": { "Homepage": "https://hpb.io" }, - "marketcap_usd": 987769, + "marketcap_usd": 957132, "name": "High Performance Blockchain", "shortcut": "HPB", "t1_enabled": "yes", @@ -35621,6 +36948,48 @@ } ] }, + "eth:HTML": { + "links": { + "Homepage": "https://htmlcoin.com" + }, + "marketcap_usd": 0, + "name": "Htmlcoin", + "shortcut": "HTML", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:HTZ": { + "links": { + "Homepage": "https://www.hertz-network.com" + }, + "marketcap_usd": 124618, + "name": "Hertz Network", + "shortcut": "HTZ", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:ILT": { "links": { "Homepage": "https://iolite.io" @@ -35642,6 +37011,27 @@ } ] }, + "eth:IMV": { + "links": { + "Homepage": "https://imversed.com" + }, + "marketcap_usd": 0, + "name": "Imversed", + "shortcut": "IMV", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:IORA": { "links": { "Homepage": "https://iorachain.com" @@ -35705,6 +37095,27 @@ } ] }, + "eth:ISLM": { + "links": { + "Homepage": "https://islamiccoin.net" + }, + "marketcap_usd": 0, + "name": "Haqq Network", + "shortcut": "ISLM", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:IVAR": { "links": { "Homepage": "https://ivarex.com" @@ -35726,6 +37137,48 @@ } ] }, + "eth:J": { + "links": { + "Homepage": "https://j.blockcoach.com:8089" + }, + "marketcap_usd": 0, + "name": "Metacodechain", + "shortcut": "J", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:JBC": { + "links": { + "Homepage": "https://jibchain.net" + }, + "marketcap_usd": 0, + "name": "JIBCHAIN L1", + "shortcut": "JBC", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:JEWEL:53935": { "links": { "Homepage": "https://defikingdoms.com" @@ -35747,6 +37200,27 @@ } ] }, + "eth:JINDA": { + "links": { + "Homepage": "https://blockchain.or.th" + }, + "marketcap_usd": 0, + "name": "TBSI", + "shortcut": "JINDA", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:JOYS": { "links": { "Homepage": "https://joys.digital" @@ -35768,6 +37242,27 @@ } ] }, + "eth:KAI": { + "links": { + "Homepage": "https://kardiachain.io" + }, + "marketcap_usd": 28444438, + "name": "KardiaChain", + "shortcut": "KAI", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:KAR": { "links": { "Homepage": "https://karura.network" @@ -35793,7 +37288,7 @@ "links": { "Homepage": "https://www.kava.io" }, - "marketcap_usd": 428263632, + "marketcap_usd": 367913718, "name": "Kava EVM", "shortcut": "KAVA", "t1_enabled": "yes", @@ -35831,11 +37326,32 @@ } ] }, + "eth:KEK": { + "links": { + "Homepage": "https://kekchain.com" + }, + "marketcap_usd": 0, + "name": "Kekchain", + "shortcut": "KEK", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:KLAY": { "links": { "Homepage": "https://www.klaytn.com/" }, - "marketcap_usd": 744892923, + "marketcap_usd": 890666429, "name": "Klaytn", "shortcut": "KLAY", "t1_enabled": "yes", @@ -35894,6 +37410,27 @@ } ] }, + "eth:KUB": { + "links": { + "Homepage": "https://www.bitkubchain.com/" + }, + "marketcap_usd": 0, + "name": "Bitkub Chain", + "shortcut": "KUB", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:L1": { "links": { "Homepage": "https://www.genesisl1.com" @@ -35957,6 +37494,27 @@ } ] }, + "eth:LAVA": { + "links": { + "Homepage": "https://elysiumscan.vulcanforged.com" + }, + "marketcap_usd": 0, + "name": "Elysium", + "shortcut": "LAVA", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:LISINS": { "links": { "Homepage": "https://lisinski.online" @@ -35978,6 +37536,27 @@ } ] }, + "eth:LUCID": { + "links": { + "Homepage": "https://lucidcoin.io" + }, + "marketcap_usd": 0, + "name": "Lucid Blockchain", + "shortcut": "LUCID", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:LUDAN": { "links": { "Homepage": "https://www.ludan.org/" @@ -35999,11 +37578,116 @@ } ] }, + "eth:LYC": { + "links": { + "Homepage": "https://lycanchain.com" + }, + "marketcap_usd": 0, + "name": "Lycan Chain", + "shortcut": "LYC", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:MAI": { + "links": { + "Homepage": "" + }, + "marketcap_usd": 0, + "name": "maistestsubnet", + "shortcut": "MAI", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:MAP": { + "links": { + "Homepage": "https://maplabs.io" + }, + "marketcap_usd": 0, + "name": "MAP", + "shortcut": "MAP", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:MARO": { + "links": { + "Homepage": "https://ma.ro/" + }, + "marketcap_usd": 27306090, + "name": "MARO Blockchain", + "shortcut": "MARO", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:MAS": { + "links": { + "Homepage": "https://masterbank.org" + }, + "marketcap_usd": 0, + "name": "Mas", + "shortcut": "MAS", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:MATH": { "links": { "Homepage": "https://mathchain.org" }, - "marketcap_usd": 10761824, + "marketcap_usd": 17614958, "name": "MathChain", "shortcut": "MATH", "t1_enabled": "yes", @@ -36024,7 +37708,7 @@ "links": { "Homepage": "https://polygon.technology/" }, - "marketcap_usd": 7431717654, + "marketcap_usd": 10673372779, "name": "Polygon", "shortcut": "MATIC", "t1_enabled": "yes", @@ -36041,11 +37725,32 @@ } ] }, + "eth:MEER": { + "links": { + "Homepage": "https://github.com/Qitmeer" + }, + "marketcap_usd": 0, + "name": "Qitmeer", + "shortcut": "MEER", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:META": { "links": { "Homepage": "https://metadium.com" }, - "marketcap_usd": 51333840, + "marketcap_usd": 61668315, "name": "Metadium", "shortcut": "META", "t1_enabled": "yes", @@ -36062,6 +37767,27 @@ } ] }, + "eth:METAD": { + "links": { + "Homepage": "https://docs.metaplayer.one/" + }, + "marketcap_usd": 0, + "name": "Metaplayerone", + "shortcut": "METAD", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:METIS": { "links": { "Homepage": "https://www.metis.io" @@ -36171,7 +37897,7 @@ "links": { "Homepage": "https://moonbeam.network/networks/moonriver/" }, - "marketcap_usd": 59768358, + "marketcap_usd": 63677633, "name": "Moonriver", "shortcut": "MOVR", "t1_enabled": "yes", @@ -36234,7 +37960,7 @@ "links": { "Homepage": "https://mtv.ac" }, - "marketcap_usd": 4083320, + "marketcap_usd": 4779941, "name": "MultiVAC", "shortcut": "MTV", "t1_enabled": "yes", @@ -36272,6 +37998,27 @@ } ] }, + "eth:MYTH": { + "links": { + "Homepage": "https://mythicalgames.com/" + }, + "marketcap_usd": 0, + "name": "Mythical Chain", + "shortcut": "MYTH", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:NEON:245022934": { "links": { "Homepage": "https://neon-labs.org" @@ -36297,7 +38044,7 @@ "links": { "Homepage": "https://www.newtonproject.org/" }, - "marketcap_usd": 4921218, + "marketcap_usd": 3728171, "name": "Newton", "shortcut": "NEW", "t1_enabled": "yes", @@ -36314,32 +38061,11 @@ } ] }, - "eth:NEXT": { - "links": { - "Homepage": "https://www.nextsmartchain.com/" - }, - "marketcap_usd": 0, - "name": "NEXT Smart Chain", - "shortcut": "NEXT", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "coin", - "wallet": [ - { - "name": "MyCrypto", - "url": "https://mycrypto.com" - }, - { - "name": "MyEtherWallet", - "url": "https://www.myetherwallet.com" - } - ] - }, "eth:NRG": { "links": { "Homepage": "https://www.energi.world/" }, - "marketcap_usd": 10456381, + "marketcap_usd": 12197414, "name": "Energi", "shortcut": "NRG", "t1_enabled": "yes", @@ -36377,6 +38103,69 @@ } ] }, + "eth:NULS": { + "links": { + "Homepage": "https://nuls.io" + }, + "marketcap_usd": 27852400, + "name": "ENULS", + "shortcut": "NULS", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:NUM": { + "links": { + "Homepage": "https://numbersprotocol.io" + }, + "marketcap_usd": 0, + "name": "Numbers", + "shortcut": "NUM", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:NetZ": { + "links": { + "Homepage": "https://mainnetz.io" + }, + "marketcap_usd": 0, + "name": "MainnetZ", + "shortcut": "NetZ", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:OAC": { "links": { "Homepage": "https://scan.oasischain.io" @@ -36398,6 +38187,48 @@ } ] }, + "eth:OCTA": { + "links": { + "Homepage": "https://octa.space" + }, + "marketcap_usd": 0, + "name": "OctaSpace", + "shortcut": "OCTA", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:OHO": { + "links": { + "Homepage": "https://oho.ai" + }, + "marketcap_usd": 0, + "name": "OHO", + "shortcut": "OHO", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:OKT": { "links": { "Homepage": "https://www.okex.com/okc" @@ -36444,7 +38275,7 @@ "links": { "Homepage": "https://oneledger.io" }, - "marketcap_usd": 2972940, + "marketcap_usd": 2321403, "name": "OneLedger", "shortcut": "OLT", "t1_enabled": "yes", @@ -36482,11 +38313,32 @@ } ] }, + "eth:OMAX": { + "links": { + "Homepage": "https://www.omaxcoin.com/" + }, + "marketcap_usd": 0, + "name": "Omax", + "shortcut": "OMAX", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:OMC": { "links": { "Homepage": "https://omchain.io" }, - "marketcap_usd": 383185, + "marketcap_usd": 2203543, "name": "omChain", "shortcut": "OMC", "t1_enabled": "yes", @@ -36507,7 +38359,7 @@ "links": { "Homepage": "https://www.harmony.one/" }, - "marketcap_usd": 236145795, + "marketcap_usd": 283158162, "name": "Harmony", "shortcut": "ONE", "t1_enabled": "yes", @@ -36528,7 +38380,7 @@ "links": { "Homepage": "https://www.harmony.one/" }, - "marketcap_usd": 236145795, + "marketcap_usd": 283158162, "name": "Harmony", "shortcut": "ONE", "t1_enabled": "yes", @@ -36549,7 +38401,7 @@ "links": { "Homepage": "https://www.harmony.one/" }, - "marketcap_usd": 236145795, + "marketcap_usd": 283158162, "name": "Harmony", "shortcut": "ONE", "t1_enabled": "yes", @@ -36570,7 +38422,7 @@ "links": { "Homepage": "https://www.harmony.one/" }, - "marketcap_usd": 236145795, + "marketcap_usd": 283158162, "name": "Harmony", "shortcut": "ONE", "t1_enabled": "yes", @@ -36591,7 +38443,7 @@ "links": { "Homepage": "https://ont.io/" }, - "marketcap_usd": 190530450, + "marketcap_usd": 220559219, "name": "Ontology", "shortcut": "ONG", "t1_enabled": "yes", @@ -36608,6 +38460,27 @@ } ] }, + "eth:ONUS": { + "links": { + "Homepage": "https://onuschain.io" + }, + "marketcap_usd": 42435453, + "name": "ONUS Chain", + "shortcut": "ONUS", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:OPC": { "links": { "Homepage": "https://www.openchain.live" @@ -36629,6 +38502,90 @@ } ] }, + "eth:OPN": { + "links": { + "Homepage": "" + }, + "marketcap_usd": 0, + "name": "Susono", + "shortcut": "OPN", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:ORL": { + "links": { + "Homepage": "https://orlchain.com" + }, + "marketcap_usd": 0, + "name": "Orlando Chain", + "shortcut": "ORL", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:OTP": { + "links": { + "Homepage": "https://parachain.origintrail.io" + }, + "marketcap_usd": 0, + "name": "OriginTrail Parachain", + "shortcut": "OTP", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:OXYN": { + "links": { + "Homepage": "https://beta.opulent-x.com" + }, + "marketcap_usd": 0, + "name": "Opulent-X BETA", + "shortcut": "OXYN", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:OY": { "links": { "Homepage": "https://www.oychain.io" @@ -36671,6 +38628,27 @@ } ] }, + "eth:PDC": { + "links": { + "Homepage": "https://ipdc.io" + }, + "marketcap_usd": 0, + "name": "PDC", + "shortcut": "PDC", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:PETH": { "links": { "Homepage": "https://primusmoney.com" @@ -36717,7 +38695,7 @@ "links": { "Homepage": "https://explorer.lightstreams.io" }, - "marketcap_usd": 45057, + "marketcap_usd": 0, "name": "Lightstreams", "shortcut": "PHT", "t1_enabled": "yes", @@ -36797,6 +38775,27 @@ } ] }, + "eth:PLQ": { + "links": { + "Homepage": "https://planq.network" + }, + "marketcap_usd": 0, + "name": "Planq", + "shortcut": "PLQ", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:PLS": { "links": { "Homepage": "https://pulsechain.com/" @@ -36822,7 +38821,7 @@ "links": { "Homepage": "https://poa.network" }, - "marketcap_usd": 5187637, + "marketcap_usd": 8185146, "name": "POA Network Core", "shortcut": "POA", "t1_enabled": "yes", @@ -36843,7 +38842,7 @@ "links": { "Homepage": "https://polis.tech" }, - "marketcap_usd": 119191, + "marketcap_usd": 92704, "name": "Polis", "shortcut": "POLIS", "t1_enabled": "yes", @@ -36860,6 +38859,27 @@ } ] }, + "eth:POM": { + "links": { + "Homepage": "https://proofofmemes.org" + }, + "marketcap_usd": 0, + "name": "Proof Of Memes", + "shortcut": "POM", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:POP": { "links": { "Homepage": "https://popcateum.org" @@ -36902,6 +38922,48 @@ } ] }, + "eth:PSC": { + "links": { + "Homepage": "https://www.polysmartchain.com/" + }, + "marketcap_usd": 0, + "name": "PolySmartChain", + "shortcut": "PSC", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:PTX": { + "links": { + "Homepage": "https://www.pandoproject.org/" + }, + "marketcap_usd": 0, + "name": "PandoProject", + "shortcut": "PTX", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:QDC": { "links": { "Homepage": "https://quadrans.io" @@ -36923,11 +38985,32 @@ } ] }, + "eth:QKA": { + "links": { + "Homepage": "https://qkacoin.org" + }, + "marketcap_usd": 0, + "name": "Quokkacoin", + "shortcut": "QKA", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:QKC:100000": { "links": { "Homepage": "https://www.quarkchain.io" }, - "marketcap_usd": 70288774, + "marketcap_usd": 74074939, "name": "QuarkChain", "shortcut": "QKC", "t1_enabled": "yes", @@ -36948,7 +39031,7 @@ "links": { "Homepage": "https://www.quarkchain.io" }, - "marketcap_usd": 70288774, + "marketcap_usd": 74074939, "name": "QuarkChain", "shortcut": "QKC", "t1_enabled": "yes", @@ -36969,7 +39052,7 @@ "links": { "Homepage": "https://www.quarkchain.io" }, - "marketcap_usd": 70288774, + "marketcap_usd": 74074939, "name": "QuarkChain", "shortcut": "QKC", "t1_enabled": "yes", @@ -36990,7 +39073,7 @@ "links": { "Homepage": "https://www.quarkchain.io" }, - "marketcap_usd": 70288774, + "marketcap_usd": 74074939, "name": "QuarkChain", "shortcut": "QKC", "t1_enabled": "yes", @@ -37011,7 +39094,7 @@ "links": { "Homepage": "https://www.quarkchain.io" }, - "marketcap_usd": 70288774, + "marketcap_usd": 74074939, "name": "QuarkChain", "shortcut": "QKC", "t1_enabled": "yes", @@ -37032,7 +39115,7 @@ "links": { "Homepage": "https://www.quarkchain.io" }, - "marketcap_usd": 70288774, + "marketcap_usd": 74074939, "name": "QuarkChain", "shortcut": "QKC", "t1_enabled": "yes", @@ -37053,7 +39136,7 @@ "links": { "Homepage": "https://www.quarkchain.io" }, - "marketcap_usd": 70288774, + "marketcap_usd": 74074939, "name": "QuarkChain", "shortcut": "QKC", "t1_enabled": "yes", @@ -37074,7 +39157,7 @@ "links": { "Homepage": "https://www.quarkchain.io" }, - "marketcap_usd": 70288774, + "marketcap_usd": 74074939, "name": "QuarkChain", "shortcut": "QKC", "t1_enabled": "yes", @@ -37095,7 +39178,7 @@ "links": { "Homepage": "https://www.quarkchain.io" }, - "marketcap_usd": 70288774, + "marketcap_usd": 74074939, "name": "QuarkChain", "shortcut": "QKC", "t1_enabled": "yes", @@ -37133,6 +39216,27 @@ } ] }, + "eth:QOM": { + "links": { + "Homepage": "https://qom.one" + }, + "marketcap_usd": 0, + "name": "QL1", + "shortcut": "QOM", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:RBD": { "links": { "Homepage": "https://www.wegochain.io" @@ -37175,11 +39279,53 @@ } ] }, + "eth:REAL": { + "links": { + "Homepage": "https://www.rclsidechain.com/" + }, + "marketcap_usd": 0, + "name": "Realchain", + "shortcut": "REAL", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:REDLC": { + "links": { + "Homepage": "https://redlight.finance/" + }, + "marketcap_usd": 0, + "name": "Redlight Chain", + "shortcut": "REDLC", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:REI:47805": { "links": { "Homepage": "https://rei.network/" }, - "marketcap_usd": 29317148, + "marketcap_usd": 32752198, "name": "REI Network", "shortcut": "REI", "t1_enabled": "yes", @@ -37196,11 +39342,32 @@ } ] }, + "eth:RESIN": { + "links": { + "Homepage": "https://resincoin.dev" + }, + "marketcap_usd": 0, + "name": "ResinCoin", + "shortcut": "RESIN", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:RING": { "links": { "Homepage": "https://darwinia.network/" }, - "marketcap_usd": 7465585, + "marketcap_usd": 7420230, "name": "Darwinia Network", "shortcut": "RING", "t1_enabled": "yes", @@ -37259,32 +39426,11 @@ } ] }, - "eth:ROSE": { - "links": { - "Homepage": "https://docs.oasis.dev/general/developer-resources/overview" - }, - "marketcap_usd": 0, - "name": "Emerald Paratime", - "shortcut": "ROSE", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "coin", - "wallet": [ - { - "name": "MyCrypto", - "url": "https://mycrypto.com" - }, - { - "name": "MyEtherWallet", - "url": "https://www.myetherwallet.com" - } - ] - }, "eth:RPG": { "links": { "Homepage": "https://rangersprotocol.com" }, - "marketcap_usd": 1399497, + "marketcap_usd": 1063873, "name": "Rangers Protocol", "shortcut": "RPG", "t1_enabled": "yes", @@ -37322,6 +39468,27 @@ } ] }, + "eth:SAMA": { + "links": { + "Homepage": "https://moonsama.com" + }, + "marketcap_usd": 0, + "name": "Exosama Network", + "shortcut": "SAMA", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:SDN": { "links": { "Homepage": "https://shiden.astar.network/" @@ -37343,6 +39510,27 @@ } ] }, + "eth:SEED": { + "links": { + "Homepage": "https://www.seedcoin.network/" + }, + "marketcap_usd": 0, + "name": "SeedCoin-Network", + "shortcut": "SEED", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:SETM": { "links": { "Homepage": "https://setheum.xyz" @@ -37427,11 +39615,32 @@ } ] }, + "eth:SINSO": { + "links": { + "Homepage": "https://sinso.io" + }, + "marketcap_usd": 0, + "name": "AmStar", + "shortcut": "SINSO", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:SKU": { "links": { "Homepage": "https://clover.finance/sakura" }, - "marketcap_usd": 904244, + "marketcap_usd": 687504, "name": "Sakura", "shortcut": "SKU", "t1_enabled": "yes", @@ -37452,7 +39661,7 @@ "links": { "Homepage": "https://smartmesh.io" }, - "marketcap_usd": 2032204, + "marketcap_usd": 2755986, "name": "SmartMesh", "shortcut": "SMT", "t1_enabled": "yes", @@ -37553,6 +39762,27 @@ } ] }, + "eth:SRDX": { + "links": { + "Homepage": "https://mysardis.com" + }, + "marketcap_usd": 0, + "name": "Sardis", + "shortcut": "SRDX", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:SRN": { "links": { "Homepage": "https://surnet.org" @@ -37574,6 +39804,69 @@ } ] }, + "eth:STAND": { + "links": { + "Homepage": "https://goldsmartchain.com" + }, + "marketcap_usd": 0, + "name": "Gold Smart Chain", + "shortcut": "STAND", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:STOS": { + "links": { + "Homepage": "https://www.thestratos.org" + }, + "marketcap_usd": 13276679, + "name": "Stratos", + "shortcut": "STOS", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:SVRN": { + "links": { + "Homepage": "https://soverun.com" + }, + "marketcap_usd": 0, + "name": "Soverun", + "shortcut": "SVRN", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:SX": { "links": { "Homepage": "https://www.sx.technology" @@ -37599,7 +39892,7 @@ "links": { "Homepage": "https://seelen.pro/" }, - "marketcap_usd": 2742038, + "marketcap_usd": 3511278, "name": "Seele", "shortcut": "Seele", "t1_enabled": "yes", @@ -37616,6 +39909,48 @@ } ] }, + "eth:SmuX": { + "links": { + "Homepage": "https://www.streamux.cloud" + }, + "marketcap_usd": 0, + "name": "StreamuX Blockchain", + "shortcut": "SmuX", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:T-EKTA": { + "links": { + "Homepage": "https://www.ekta.io" + }, + "marketcap_usd": 0, + "name": "T-EKTA", + "shortcut": "T-EKTA", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:TAO": { "links": { "Homepage": "https://tao.network" @@ -37763,6 +40098,27 @@ } ] }, + "eth:TMY": { + "links": { + "Homepage": "https://tmychain.org/" + }, + "marketcap_usd": 0, + "name": "TMY Chain", + "shortcut": "TMY", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:TOMB": { "links": { "Homepage": "https://tombchain.com/" @@ -37788,7 +40144,7 @@ "links": { "Homepage": "https://tomochain.com" }, - "marketcap_usd": 38639467, + "marketcap_usd": 40653633, "name": "TomoChain", "shortcut": "TOMO", "t1_enabled": "yes", @@ -37893,7 +40249,7 @@ "links": { "Homepage": "https://thundercore.com" }, - "marketcap_usd": 46482691, + "marketcap_usd": 53233013, "name": "ThunderCore", "shortcut": "TT", "t1_enabled": "yes", @@ -37910,6 +40266,48 @@ } ] }, + "eth:TWL": { + "links": { + "Homepage": "https://twala.io/" + }, + "marketcap_usd": 0, + "name": "Jellie", + "shortcut": "TWL", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:TXDC": { + "links": { + "Homepage": "https://xinfin.org" + }, + "marketcap_usd": 0, + "name": "XDC Apothem Network", + "shortcut": "TXDC", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:TXL": { "links": { "Homepage": "https://autobahn.network" @@ -37977,7 +40375,7 @@ "links": { "Homepage": "https://ubiqsmart.com" }, - "marketcap_usd": 1797469, + "marketcap_usd": 1302549, "name": "Ubiq", "shortcut": "UBQ", "t1_enabled": "yes", @@ -37994,6 +40392,48 @@ } ] }, + "eth:ULX": { + "links": { + "Homepage": "https://ultron.foundation" + }, + "marketcap_usd": 0, + "name": "Ultron", + "shortcut": "ULX", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:UNQ": { + "links": { + "Homepage": "https://unique.network" + }, + "marketcap_usd": 0, + "name": "Unique", + "shortcut": "UNQ", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:UZMI": { "links": { "Homepage": "https://uzmigames.com.br/" @@ -38036,6 +40476,48 @@ } ] }, + "eth:VET": { + "links": { + "Homepage": "https://vechain.org" + }, + "marketcap_usd": 2002440234, + "name": "VeChain", + "shortcut": "VET", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:VETH": { + "links": { + "Homepage": "https://www.openvessel.io" + }, + "marketcap_usd": 0, + "name": "OpenVessel", + "shortcut": "VETH", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:VLX": { "links": { "Homepage": "https://velas.com" @@ -38078,6 +40560,27 @@ } ] }, + "eth:VNT": { + "links": { + "Homepage": "https://ventionscan.io" + }, + "marketcap_usd": 0, + "name": "Vention Smart Chain", + "shortcut": "VNT", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:VS:888888": { "links": { "Homepage": "https://www.v.network" @@ -38099,6 +40602,27 @@ } ] }, + "eth:VSC": { + "links": { + "Homepage": "https://vsc-dataseed.vyvo.org" + }, + "marketcap_usd": 0, + "name": "Vyvo Smart Chain", + "shortcut": "VSC", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:W3G": { "links": { "Homepage": "https://web3games.org/" @@ -38145,7 +40669,7 @@ "links": { "Homepage": "https://www.wanscan.org" }, - "marketcap_usd": 34185865, + "marketcap_usd": 58286398, "name": "Wanchain", "shortcut": "WAN", "t1_enabled": "yes", @@ -38187,9 +40711,30 @@ } ] }, + "eth:WEMIX": { + "links": { + "Homepage": "https://wemix.com" + }, + "marketcap_usd": 525753922, + "name": "WEMIX3.0", + "shortcut": "WEMIX", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:WGM": { "links": { - "Homepage": "https://trywagmi.xyz" + "Homepage": "https://subnets-test.avax.network/wagmi/details" }, "marketcap_usd": 0, "name": "WAGMI", @@ -38234,7 +40779,7 @@ "Homepage": "https://xinfin.org" }, "marketcap_usd": 0, - "name": "XinFin Network", + "name": "XinFin XDC Network", "shortcut": "XDC", "t1_enabled": "yes", "t2_enabled": "yes", @@ -38267,6 +40812,48 @@ } ] }, + "eth:XETA": { + "links": { + "Homepage": "https://xanachain.xana.net/" + }, + "marketcap_usd": 0, + "name": "XANAChain", + "shortcut": "XETA", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:XODEX": { + "links": { + "Homepage": "https://xo-dex.com" + }, + "marketcap_usd": 0, + "name": "XODEX", + "shortcut": "XODEX", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:XT": { "links": { "Homepage": "https://xsc.pub/" @@ -38309,6 +40896,27 @@ } ] }, + "eth:XZO": { + "links": { + "Homepage": "https://exzo.network" + }, + "marketcap_usd": 0, + "name": "Exzo Network", + "shortcut": "XZO", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:YCC": { "links": { "Homepage": "https://www.yuan.org" @@ -38351,6 +40959,27 @@ } ] }, + "eth:ZENIQ": { + "links": { + "Homepage": "https://www.zeniq.dev/" + }, + "marketcap_usd": 0, + "name": "Zeniq", + "shortcut": "ZENIQ", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:ZENITH": { "links": { "Homepage": "https://www.zenithchain.co/" @@ -38376,7 +41005,7 @@ "links": { "Homepage": "https://www.singularity.gold" }, - "marketcap_usd": 103580, + "marketcap_usd": 84248, "name": "Singularity ZERO", "shortcut": "ZERO", "t1_enabled": "yes", @@ -38393,6 +41022,48 @@ } ] }, + "eth:ZETA": { + "links": { + "Homepage": "https://docs.zetachain.com/" + }, + "marketcap_usd": 0, + "name": "ZetaChain", + "shortcut": "ZETA", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:ZTH:427": { + "links": { + "Homepage": "" + }, + "marketcap_usd": 0, + "name": "Zeeth Chain", + "shortcut": "ZTH", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:ZYX": { "links": { "Homepage": "https://zyx.network/" @@ -38477,6 +41148,48 @@ } ] }, + "eth:eBTC": { + "links": { + "Homepage": "https://bitcoinevm.com" + }, + "marketcap_usd": 0, + "name": "Bitcoin EVM", + "shortcut": "eBTC", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:hP2": { + "links": { + "Homepage": "https://p12.network" + }, + "marketcap_usd": 0, + "name": "P12 Chain", + "shortcut": "hP2", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:jfin": { "links": { "Homepage": "https://jfinchain.com" @@ -38502,7 +41215,7 @@ "links": { "Homepage": "https://www.platon.network" }, - "marketcap_usd": 20556182, + "marketcap_usd": 78424786, "name": "PlatON", "shortcut": "lat", "t1_enabled": "yes", @@ -38540,6 +41253,69 @@ } ] }, + "eth:mALGO": { + "links": { + "Homepage": "https://milkomeda.com" + }, + "marketcap_usd": 0, + "name": "Milkomeda A1", + "shortcut": "mALGO", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:mc": { + "links": { + "Homepage": "https://moac.io" + }, + "marketcap_usd": 0, + "name": "MOAC", + "shortcut": "mc", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:nSAN": { + "links": { + "Homepage": "https://sanr.app" + }, + "marketcap_usd": 0, + "name": "SanR Chain", + "shortcut": "nSAN", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, "eth:pCKB": { "links": { "Homepage": "https://www.nervos.org" @@ -38582,13 +41358,13 @@ } ] }, - "eth:tGOR": { + "eth:tBVE": { "links": { - "Homepage": "https://goerli.net/#about" + "Homepage": "https://beverlyhills.ethdevops.io" }, "marketcap_usd": 0, - "name": "G\u00f6rli", - "shortcut": "tGOR", + "name": "Beverly Hills", + "shortcut": "tBVE", "t1_enabled": "yes", "t2_enabled": "yes", "type": "coin", @@ -38603,13 +41379,13 @@ } ] }, - "eth:tKOR": { + "eth:tCRC": { "links": { - "Homepage": "https://optimism.io" + "Homepage": "https://github.com/ethereum-pocr/kerleano" }, "marketcap_usd": 0, - "name": "Optimism Kovan", - "shortcut": "tKOR", + "name": "Kerleano", + "shortcut": "tCRC", "t1_enabled": "yes", "t2_enabled": "yes", "type": "coin", @@ -38624,13 +41400,139 @@ } ] }, - "eth:tKOV": { + "eth:tETH:11155111": { + "links": { + "Homepage": "https://sepolia.otterscan.io" + }, + "marketcap_usd": 0, + "name": "Sepolia", + "shortcut": "tETH", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:tETH:3": { + "links": { + "Homepage": "https://github.com/ethereum/ropsten" + }, + "marketcap_usd": 0, + "name": "Ropsten", + "shortcut": "tETH", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:tETH:4": { + "links": { + "Homepage": "https://www.rinkeby.io" + }, + "marketcap_usd": 0, + "name": "Rinkeby", + "shortcut": "tETH", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:tETH:42": { "links": { "Homepage": "https://kovan-testnet.github.io/website" }, "marketcap_usd": 0, "name": "Kovan", - "shortcut": "tKOV", + "shortcut": "tETH", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:tETH:5": { + "links": { + "Homepage": "https://goerli.net/#about" + }, + "marketcap_usd": 0, + "name": "Goerli", + "shortcut": "tETH", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:tKEK": { + "links": { + "Homepage": "https://kekchain.com" + }, + "marketcap_usd": 0, + "name": "Kekchain (kektest)", + "shortcut": "tKEK", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": [ + { + "name": "MyCrypto", + "url": "https://mycrypto.com" + }, + { + "name": "MyEtherWallet", + "url": "https://www.myetherwallet.com" + } + ] + }, + "eth:tMAP": { + "links": { + "Homepage": "https://maplabs.io" + }, + "marketcap_usd": 0, + "name": "MAP Makalu", + "shortcut": "tMAP", "t1_enabled": "yes", "t2_enabled": "yes", "type": "coin", @@ -38666,13 +41568,13 @@ } ] }, - "eth:tRIN": { + "eth:tWIRE": { "links": { - "Homepage": "https://www.rinkeby.io" + "Homepage": "https://wireshape.org" }, "marketcap_usd": 0, - "name": "Rinkeby", - "shortcut": "tRIN", + "name": "Floripa", + "shortcut": "tWIRE", "t1_enabled": "yes", "t2_enabled": "yes", "type": "coin", @@ -38687,30 +41589,13 @@ } ] }, - "eth:tROP": { + "eth:taro": { "links": { - "Homepage": "https://github.com/ethereum/ropsten" + "Homepage": "https://j2o.io" }, "marketcap_usd": 0, - "name": "Ropsten", - "shortcut": "tROP", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "coin", - "wallet": [ - { - "name": "Trezor Suite", - "url": "https://suite.trezor.io" - } - ] - }, - "eth:tSEP": { - "links": { - "Homepage": "https://sepolia.otterscan.io" - }, - "marketcap_usd": 0, - "name": "Sepolia", - "shortcut": "tSEP", + "name": "J2O Taro", + "shortcut": "taro", "t1_enabled": "yes", "t2_enabled": "yes", "type": "coin", @@ -38727,10 +41612,10 @@ }, "eth:xDAI:100": { "links": { - "Homepage": "https://developers.gnosischain.com" + "Homepage": "https://docs.gnosischain.com" }, - "marketcap_usd": 10886195, - "name": "Gnosis Chain", + "marketcap_usd": 6301880, + "name": "Gnosis", "shortcut": "xDAI", "t1_enabled": "yes", "t2_enabled": "yes", @@ -38750,8 +41635,8 @@ "links": { "Homepage": "https://www.xdaichain.com/for-developers/optimism-optimistic-rollups-on-gc" }, - "marketcap_usd": 10886195, - "name": "Optimism on Gnosis Chain", + "marketcap_usd": 6301880, + "name": "Optimism on Gnosis", "shortcut": "xDAI", "t1_enabled": "yes", "t2_enabled": "yes", @@ -38767,13 +41652,13 @@ } ] }, - "eth:\u03a6": { + "eth:xlon": { "links": { - "Homepage": "https://phi.network" + "Homepage": "https://xlon.org" }, "marketcap_usd": 0, - "name": "PHI Network", - "shortcut": "\u03a6", + "name": "Excelon", + "shortcut": "xlon", "t1_enabled": "yes", "t2_enabled": "yes", "type": "coin", @@ -38814,7 +41699,7 @@ "Github": "https://github.com/input-output-hk/cardano-node", "Homepage": "https://www.cardano.org" }, - "marketcap_usd": 13465469418, + "marketcap_usd": 12578496280, "name": "Cardano", "shortcut": "ADA", "t1_enabled": "no", @@ -38831,7 +41716,7 @@ "links": { "Homepage": "https://binance.org" }, - "marketcap_usd": 51094687147, + "marketcap_usd": 47729156144, "name": "Binance Chain", "shortcut": "BNB", "t1_enabled": "no", @@ -38849,7 +41734,7 @@ "Github": "https://github.com/EOSIO/eos", "Homepage": "https://eos.io" }, - "marketcap_usd": 1145362807, + "marketcap_usd": 1253975568, "name": "EOS", "shortcut": "EOS", "t1_enabled": "no", @@ -38867,7 +41752,7 @@ "Github": "https://github.com/maidsafe", "Homepage": "https://maidsafe.net" }, - "marketcap_usd": 62465583, + "marketcap_usd": 58807837, "name": "MaidSafeCoin", "shortcut": "MAID", "t1_enabled": "yes", @@ -38880,7 +41765,7 @@ "Github": "https://github.com/OmniLayer", "Homepage": "https://www.omnilayer.org" }, - "marketcap_usd": 900903, + "marketcap_usd": 551822, "name": "Omni", "shortcut": "OMNI", "t1_enabled": "yes", @@ -38892,7 +41777,7 @@ "links": { "Homepage": "https://tether.to" }, - "marketcap_usd": 69415999585, + "marketcap_usd": 70941600691, "name": "Tether", "shortcut": "USDT", "t1_enabled": "yes", @@ -38905,7 +41790,7 @@ "Github": "https://github.com/stellar/stellar-core", "Homepage": "https://www.stellar.org" }, - "marketcap_usd": 2772490697, + "marketcap_usd": 2316269327, "name": "Stellar", "shortcut": "XLM", "t1_enabled": "yes", @@ -38931,7 +41816,7 @@ "Github": "https://github.com/monero-project/monero", "Homepage": "https://getmonero.org" }, - "marketcap_usd": 2729162888, + "marketcap_usd": 2733395377, "name": "Monero", "shortcut": "XMR", "t1_enabled": "no", @@ -38949,7 +41834,7 @@ "Github": "https://github.com/ripple/rippled", "Homepage": "https://ripple.com" }, - "marketcap_usd": 22723856105, + "marketcap_usd": 19193526787, "name": "Ripple", "shortcut": "XRP", "t1_enabled": "no", @@ -38971,7 +41856,7 @@ "Github": "https://github.com/tezos/tezos", "Homepage": "https://tezos.com" }, - "marketcap_usd": 1274246127, + "marketcap_usd": 1094195402, "name": "Tezos", "shortcut": "XTZ", "t1_enabled": "no", @@ -38979,8 +41864,8 @@ "type": "coin", "wallet": [ { - "name": "SimpleStaking", - "url": "https://simplestaking.com" + "name": "Briskett", + "url": "https://briskett.app" } ] }, @@ -39074,7 +41959,7 @@ "links": { "Homepage": "https://nemplatform.com" }, - "marketcap_usd": 343868874, + "marketcap_usd": 475266269, "name": "NEM", "shortcut": "XEM", "t1_enabled": "yes", @@ -39089,12 +41974,12 @@ } }, "info": { - "marketcap_supported": "108.72 %", - "marketcap_usd": 1091938540074, - "t1_coins": 1969, - "t2_coins": 1975, - "total_marketcap_usd": 1004395134375, - "updated_at": 1667386190, - "updated_at_readable": "Wed Nov 2 11:49:50 2022" + "marketcap_supported": "103.22 %", + "marketcap_usd": 1102849874078, + "t1_coins": 2106, + "t2_coins": 2112, + "total_marketcap_usd": 1068459286951, + "updated_at": 1677585029, + "updated_at_readable": "Tue Feb 28 12:50:29 2023" } } diff --git a/common/defs/ethereum/chains b/common/defs/ethereum/chains index 4ba4ca129c..805ae42ecc 160000 --- a/common/defs/ethereum/chains +++ b/common/defs/ethereum/chains @@ -1 +1 @@ -Subproject commit 4ba4ca129c4d8b1b01179bfa50ad74c2a5b51b72 +Subproject commit 805ae42ecc53aa6493949b1e9c1da41e036c1845 diff --git a/common/defs/ethereum/tokens b/common/defs/ethereum/tokens index 6a7ec4967b..0eeaf9b9f1 160000 --- a/common/defs/ethereum/tokens +++ b/common/defs/ethereum/tokens @@ -1 +1 @@ -Subproject commit 6a7ec4967b797f90e024296ef27c58480bf1b8ca +Subproject commit 0eeaf9b9f13b5e6538da26d079e2b968dc8bb23f diff --git a/common/defs/fido/apple.json b/common/defs/fido/apple.json new file mode 100644 index 0000000000..26c3e1f02f --- /dev/null +++ b/common/defs/fido/apple.json @@ -0,0 +1,5 @@ +{ + "name": "Apple", + "webauthn": ["apple.com"], + "use_self_attestation": false +} diff --git a/common/defs/fido/apple.png b/common/defs/fido/apple.png new file mode 100644 index 0000000000..8145a493d5 Binary files /dev/null and b/common/defs/fido/apple.png differ diff --git a/common/defs/fido/google.json b/common/defs/fido/google.json index c3a08976d5..a8071c30fe 100644 --- a/common/defs/fido/google.json +++ b/common/defs/fido/google.json @@ -6,5 +6,6 @@ "label": "google.com" } ], - "webauthn": ["google.com"] + "webauthn": ["google.com"], + "use_self_attestation": false } diff --git a/common/defs/misc/misc.json b/common/defs/misc/misc.json index e2486aa87e..bc6f8f17ae 100644 --- a/common/defs/misc/misc.json +++ b/common/defs/misc/misc.json @@ -87,7 +87,7 @@ } }, { - "name": "Cardano Testnet", + "name": "Cardano Preview Testnet", "shortcut": "tADA", "slip44": 1815, "curve": "ed25519", diff --git a/common/defs/support.json b/common/defs/support.json index 74ed794e1c..f67b1caf60 100644 --- a/common/defs/support.json +++ b/common/defs/support.json @@ -59,10 +59,7 @@ "eth:PIRL": true, "eth:RBTC": true, "eth:UBQ": true, - "eth:tKOV": true, "eth:tRBTC": true, - "eth:tRIN": true, - "eth:tROP": true, "misc:ADA": true, "misc:BNB": true, "misc:EOS": true, @@ -96,7 +93,6 @@ "bitcoin:ZEC": true, "eth:ETC": true, "eth:ETH:1": true, - "eth:tROP": true, "misc:XRP": true, "misc:tXRP": true }, @@ -159,8 +155,8 @@ "bitcoin:tQTUM": "1.8.1", "bitcoin:tRVN": "1.10.0", "bitcoin:tSMART": "1.7.1", - "erc20:avax:AVAX": "1.11.2", "erc20:avax:USDT": "1.11.2", + "erc20:avax:WAVAX": "1.11.3", "erc20:esn:DGT": "1.7.1", "erc20:esn:TOPM": "1.7.1", "erc20:etc:BEC": "1.6.2", @@ -196,9 +192,7 @@ "erc20:eth:ADL": "1.6.2", "erc20:eth:ADST": "1.6.2", "erc20:eth:ADT": "1.6.2", - "erc20:eth:ADX": "1.6.2", "erc20:eth:ADX-LOYALTY": "1.9.5", - "erc20:eth:ADXL": "1.9.5", "erc20:eth:AE": "1.6.2", "erc20:eth:AEUR": "1.9.0", "erc20:eth:AFA": "1.8.0", @@ -487,6 +481,7 @@ "erc20:eth:DALC": "1.6.2", "erc20:eth:DAN": "1.6.2", "erc20:eth:DAT": "1.6.2", + "erc20:eth:DATA": "1.11.3", "erc20:eth:DATABroker": "1.6.2", "erc20:eth:DATX": "1.8.0", "erc20:eth:DAV": "1.6.2", @@ -699,6 +694,7 @@ "erc20:eth:GIF": "1.6.2", "erc20:eth:GIM": "1.6.2", "erc20:eth:GIRL": "1.8.4", + "erc20:eth:GIV": "1.11.3", "erc20:eth:GL": "1.9.1", "erc20:eth:GLA": "1.8.0", "erc20:eth:GMB": "1.11.2", @@ -720,6 +716,7 @@ "erc20:eth:GSE": "1.6.3", "erc20:eth:GST2": "1.9.0", "erc20:eth:GTC": "1.6.2", + "erc20:eth:GTEC": "1.11.3", "erc20:eth:GTH": "1.11.2", "erc20:eth:GTKT": "1.6.2", "erc20:eth:GTO": "1.6.2", @@ -887,7 +884,7 @@ "erc20:eth:LOOK": "1.6.3", "erc20:eth:LOOKS": "1.11.2", "erc20:eth:LOOM": "1.6.2", - "erc20:eth:LOOMOLD": "1.10.3", + "erc20:eth:LOOM:deprecated": "1.11.3", "erc20:eth:LOVE": "1.6.3", "erc20:eth:LPT": "1.6.2", "erc20:eth:LQD": "1.9.0", @@ -922,7 +919,6 @@ "erc20:eth:MEME": "1.9.5", "erc20:eth:MESG": "1.8.4", "erc20:eth:MEST": "1.6.2", - "erc20:eth:MET": "1.8.0", "erc20:eth:METM": "1.8.0", "erc20:eth:MFG": "1.6.2", "erc20:eth:MFT": "1.6.3", @@ -1070,7 +1066,6 @@ "erc20:eth:PATENTS": "1.6.2", "erc20:eth:PATH": "1.6.2", "erc20:eth:PATR": "1.9.0", - "erc20:eth:PAX": "1.8.0", "erc20:eth:PAXG": "1.8.4", "erc20:eth:PAY": "1.6.2", "erc20:eth:PBL": "1.6.2", @@ -1269,8 +1264,7 @@ "erc20:eth:SNM": "1.6.2", "erc20:eth:SNOV": "1.6.2", "erc20:eth:SNT": "1.6.2", - "erc20:eth:SNTR": "1.8.0", - "erc20:eth:SNX:c011": "1.10.3", + "erc20:eth:SNX": "1.11.3", "erc20:eth:SOAR": "1.8.0", "erc20:eth:SOC": "1.8.0", "erc20:eth:SOCKS": "1.9.0", @@ -1294,7 +1288,6 @@ "erc20:eth:SPZ": "1.9.0", "erc20:eth:SRN": "1.6.2", "erc20:eth:SRX": "1.8.4", - "erc20:eth:SSH": "1.6.2", "erc20:eth:SSP": "1.8.0", "erc20:eth:STABIT": "1.6.3", "erc20:eth:STAC": "1.6.2", @@ -1325,6 +1318,7 @@ "erc20:eth:SWASH": "1.10.4", "erc20:eth:SWFTC": "1.8.0", "erc20:eth:SWM": "1.6.2", + "erc20:eth:SWRM": "1.11.3", "erc20:eth:SWT": "1.6.2", "erc20:eth:SXDT": "1.6.2", "erc20:eth:SXR": "1.9.0", @@ -1333,6 +1327,7 @@ "erc20:eth:SYS": "1.10.6", "erc20:eth:SYSX": "1.9.5", "erc20:eth:SenSatorI": "1.6.2", + "erc20:eth:Skey": "1.11.3", "erc20:eth:TALAO": "1.8.0", "erc20:eth:TAN": "1.9.0", "erc20:eth:TAU": "1.6.2", @@ -1350,6 +1345,7 @@ "erc20:eth:TCST": "1.8.4", "erc20:eth:TCT": "1.8.4", "erc20:eth:TDH": "1.6.2", + "erc20:eth:TDX": "1.11.3", "erc20:eth:TEAM": "1.8.0", "erc20:eth:TECH": "1.9.0", "erc20:eth:TEN": "1.8.0", @@ -1390,6 +1386,7 @@ "erc20:eth:TRCT": "1.8.0", "erc20:eth:TRDT": "1.8.0", "erc20:eth:TRST": "1.6.2", + "erc20:eth:TRUCCO": "1.11.3", "erc20:eth:TRYB": "1.9.0", "erc20:eth:TSW": "1.6.3", "erc20:eth:TTA": "1.8.0", @@ -1420,6 +1417,7 @@ "erc20:eth:USD-G": "1.9.0", "erc20:eth:USDC": "1.7.1", "erc20:eth:USDM": "1.6.2", + "erc20:eth:USDP": "1.11.3", "erc20:eth:USDS": "1.8.0", "erc20:eth:USDT": "1.8.0", "erc20:eth:USDx": "1.9.0", @@ -1478,6 +1476,7 @@ "erc20:eth:WCK": "1.9.0", "erc20:eth:WCN": "1.6.2", "erc20:eth:WCT": "1.6.2", + "erc20:eth:WDOGE": "1.11.3", "erc20:eth:WEB": "1.8.0", "erc20:eth:WETH": "1.6.2", "erc20:eth:WHEN": "1.6.2", @@ -1512,6 +1511,7 @@ "erc20:eth:XCHF": "1.8.0", "erc20:eth:XCL": "1.9.0", "erc20:eth:XCLR": "1.8.0", + "erc20:eth:XDATA": "1.11.3", "erc20:eth:XDCE": "1.8.0", "erc20:eth:XES": "1.8.0", "erc20:eth:XET": "1.8.0", @@ -1638,6 +1638,7 @@ "erc20:eth:eosDAC": "1.6.2", "erc20:eth:fstETHDAI": "1.9.1", "erc20:eth:iBAT": "1.9.0", + "erc20:eth:iBBT": "1.11.3", "erc20:eth:iBNB": "1.9.0", "erc20:eth:iBTC": "1.9.0", "erc20:eth:iCEX": "1.9.0", @@ -1996,8 +1997,8 @@ "erc20:rop:RLC": "1.11.2", "erc20:rop:dqr30": "1.8.0", "erc20:rsk:ARSCB": "1.10.4", + "erc20:rsk:BITP": "1.11.3", "erc20:rsk:BOBCB": "1.10.4", - "erc20:rsk:BPro": "1.10.4", "erc20:rsk:BRLCB": "1.10.4", "erc20:rsk:BRZ": "1.10.4", "erc20:rsk:BTCCB": "1.10.4", @@ -2046,88 +2047,142 @@ "erc20:ubq:SPHR": "1.10.6", "erc20:ubq:SPHRC": "1.10.6", "erc20:ubq:TGE1": "1.10.6", + "eth:$BNI": "1.11.3", "eth:$OC": "1.10.5", + "eth:0XT": "1.11.3", "eth:0xF": "1.9.4", - "eth:420": "1.9.5", "eth:AAC": "1.10.6", "eth:ACA": "1.9.5", + "eth:ACE": "1.11.3", "eth:AIOZ": "1.11.2", "eth:AITD": "1.11.2", "eth:AKA": "1.6.3", + "eth:ALOT": "1.11.3", + "eth:ALPH": "1.11.3", + "eth:ALT": "1.11.3", + "eth:ALYX": "1.11.3", + "eth:AMAX": "1.11.3", + "eth:AMB": "1.11.3", "eth:AMBROS": "1.11.2", "eth:AME": "1.11.2", + "eth:ANY": "1.11.3", "eth:APTA": "1.11.3", "eth:AQUA": "1.8.2", + "eth:AR\u00c9V": "1.11.3", + "eth:ASA": "1.11.3", "eth:ASK": "1.10.4", "eth:ASTR": "1.11.2", + "eth:ATLR": "1.11.3", "eth:ATS": "1.8.0", "eth:AUX": "1.8.4", "eth:AVAX": "1.9.5", + "eth:AVS": "1.11.3", "eth:BCS": "1.10.6", "eth:BELLY": "1.11.2", + "eth:BFC": "1.11.3", + "eth:BG": "1.11.3", + "eth:BIT": "1.11.3", + "eth:BITCI": "1.11.3", "eth:BNB": "1.9.4", - "eth:BOBA": "1.11.2", + "eth:BOA": "1.11.3", + "eth:BOMB": "1.11.3", "eth:BOY": "1.10.6", "eth:BRB": "1.11.2", - "eth:BRO": "1.10.6", + "eth:BRNKC": "1.11.3", "eth:BTA": "1.10.4", "eth:BTCIX": "1.10.5", "eth:BTM": "1.10.5", + "eth:BTON": "1.11.3", "eth:BTT": "1.10.4", + "eth:BTY": "1.11.3", + "eth:BXN": "1.11.3", "eth:Brise": "1.11.2", + "eth:CAM:500": "1.11.3", + "eth:CANTO": "1.11.3", "eth:CATE": "1.10.3", + "eth:CCNA": "1.11.3", "eth:CCP": "1.10.6", "eth:CELO": "1.9.5", "eth:CEM": "1.11.2", "eth:CFX": "1.10.6", + "eth:CIC": "1.11.3", "eth:CLASS": "1.10.6", + "eth:CLD": "1.11.3", "eth:CLO": "1.6.2", "eth:CLV": "1.10.3", + "eth:CMEMO": "1.11.3", + "eth:CMP": "1.11.3", "eth:CNDL": "1.11.2", + "eth:CONDOR": "1.11.3", + "eth:CORE": "1.11.3", "eth:CPAY:21337": "1.11.2", "eth:CPAY:3000": "1.11.2", "eth:CPAY:3001": "1.11.2", "eth:CRAB": "1.10.5", + "eth:CRC": "1.11.3", + "eth:CREDIT": "1.11.3", "eth:CRO": "1.10.5", "eth:CSB": "1.11.2", + "eth:CTEX": "1.11.3", "eth:CUBE": "1.11.2", "eth:CWN": "1.11.2", "eth:DAX": "1.10.3", + "eth:DEB": "1.11.3", + "eth:DEL": "1.11.3", "eth:DEV": "1.10.4", - "eth:DGCC": "1.11.2", + "eth:DFI": "1.11.3", "eth:DIODE": "1.9.5", - "eth:DTH": "1.10.6", + "eth:DKN": "1.11.3", + "eth:DOGS": "1.11.3", + "eth:DOINX": "1.11.3", + "eth:DOS": "1.11.3", + "eth:DRAC": "1.11.3", "eth:DWU": "1.10.3", "eth:DX": "1.11.2", + "eth:DXT": "1.11.3", "eth:DYNO": "1.10.6", + "eth:Deh": "1.11.3", + "eth:ECG": "1.11.3", "eth:ECO": "1.10.4", "eth:ECS": "1.10.6", "eth:EDG": "1.10.3", + "eth:EGAZ": "1.11.3", "eth:EGEM": "1.6.2", "eth:EIDI": "1.10.6", + "eth:EKTA": "1.11.3", "eth:ELA:20": "1.10.3", "eth:ELV": "1.10.4", + "eth:EMPIRE": "1.11.3", "eth:ENTER": "1.10.6", "eth:ES": "1.10.3", "eth:ESN": "1.6.3", "eth:ETC": "1.6.2", "eth:ETH:1": "1.6.2", + "eth:ETHF": "1.11.3", "eth:ETHO": "1.6.3", "eth:ETI": "1.8.2", "eth:ETL": "1.10.3", + "eth:ETMP": "1.11.3", "eth:ETND": "1.11.2", "eth:EUN": "1.11.2", "eth:EVA": "1.10.6", "eth:EVC": "1.10.3", "eth:EVMOS": "1.10.6", "eth:EWT": "1.9.4", + "eth:EXL": "1.11.3", "eth:EXP": "1.6.2", "eth:EZC": "1.11.2", "eth:FETH": "1.10.3", + "eth:FIL": "1.11.3", "eth:FIN": "1.9.4", + "eth:FIRE:529": "1.11.3", + "eth:FITFI": "1.11.3", + "eth:FLA": "1.11.3", "eth:FLR": "1.9.5", - "eth:FRA": "1.11.2", + "eth:FNCY": "1.11.3", + "eth:FREN": "1.11.3", "eth:FSN": "1.10.3", + "eth:FST": "1.11.3", "eth:FTM": "1.9.4", "eth:FUSE": "1.9.4", "eth:FX": "1.11.2", @@ -2135,37 +2190,61 @@ "eth:GCD": "1.11.2", "eth:GEN": "1.10.3", "eth:GLMR": "1.10.3", + "eth:GLQ": "1.11.3", + "eth:GMMT": "1.11.3", "eth:GNC": "1.10.6", "eth:GO": "1.6.2", - "eth:GT": "1.10.3", + "eth:GOLDT": "1.11.3", + "eth:GZN": "1.11.3", "eth:GooD": "1.10.3", "eth:HAIC": "1.10.3", + "eth:HBAR:295": "1.11.3", + "eth:HMND": "1.11.3", "eth:HO": "1.10.5", "eth:HOO": "1.11.2", "eth:HOP": "1.10.5", "eth:HPB": "1.8.2", "eth:HT": "1.9.5", + "eth:HTML": "1.11.3", + "eth:HTZ": "1.11.3", "eth:ILT": "1.9.4", + "eth:IMV": "1.11.3", "eth:IORA": "1.10.6", "eth:IOTX": "1.10.3", "eth:IPOS": "1.9.4", + "eth:ISLM": "1.11.3", "eth:IVAR": "1.11.2", + "eth:J": "1.11.3", + "eth:JBC": "1.11.3", "eth:JEWEL:53935": "1.10.6", + "eth:JINDA": "1.11.3", "eth:JOYS": "1.9.5", + "eth:KAI": "1.11.3", "eth:KAR": "1.9.5", "eth:KAVA": "1.11.2", "eth:KCS": "1.10.3", + "eth:KEK": "1.11.3", "eth:KLAY": "1.9.5", "eth:KSX": "1.11.2", "eth:KTO:2559": "1.10.3", + "eth:KUB": "1.11.3", "eth:L1": "1.10.5", "eth:L99": "1.10.4", "eth:LA": "1.10.6", + "eth:LAVA": "1.11.3", "eth:LISINS": "1.11.2", + "eth:LUCID": "1.11.3", "eth:LUDAN": "1.11.2", + "eth:LYC": "1.11.3", + "eth:MAI": "1.11.3", + "eth:MAP": "1.11.3", + "eth:MARO": "1.11.3", + "eth:MAS": "1.11.3", "eth:MATH": "1.9.5", "eth:MATIC": "1.9.4", + "eth:MEER": "1.11.3", "eth:META": "1.8.2", + "eth:METAD": "1.11.3", "eth:METIS": "1.10.5", "eth:MINTME": "1.10.3", "eth:MIX": "1.7.2", @@ -2176,37 +2255,54 @@ "eth:MTT": "1.10.3", "eth:MTV": "1.11.2", "eth:MUSIC": "1.6.3", + "eth:MYTH": "1.11.3", "eth:NEON:245022934": "1.10.4", "eth:NEW": "1.9.5", - "eth:NEXT": "1.10.5", "eth:NRG": "1.9.4", "eth:NTT": "1.10.5", + "eth:NULS": "1.11.3", + "eth:NUM": "1.11.3", + "eth:NetZ": "1.11.3", "eth:OAC": "1.11.2", + "eth:OCTA": "1.11.3", + "eth:OHO": "1.11.3", "eth:OKT": "1.9.5", "eth:OLO": "1.10.3", "eth:OLT": "1.10.4", "eth:OM": "1.11.2", + "eth:OMAX": "1.11.3", "eth:OMC": "1.11.2", "eth:ONE:1666600000": "1.9.5", "eth:ONE:1666600001": "1.9.5", "eth:ONE:1666600002": "1.9.5", "eth:ONE:1666600003": "1.9.5", "eth:ONG": "1.10.3", + "eth:ONUS": "1.11.3", "eth:OPC": "1.11.2", + "eth:OPN": "1.11.3", + "eth:ORL": "1.11.3", + "eth:OTP": "1.11.3", + "eth:OXYN": "1.11.3", "eth:OY": "1.10.6", "eth:PALM": "1.9.5", + "eth:PDC": "1.11.3", "eth:PETH": "1.10.3", "eth:PFT:909": "1.11.2", "eth:PHT": "1.9.4", "eth:PHX": "1.10.6", "eth:PIRL": "1.6.3", "eth:PIX": "1.10.5", + "eth:PLQ": "1.11.3", "eth:PLS": "1.10.4", "eth:POA": "1.9.4", "eth:POLIS": "1.10.6", + "eth:POM": "1.11.3", "eth:POP": "1.10.4", "eth:PRB": "1.10.6", + "eth:PSC": "1.11.3", + "eth:PTX": "1.11.3", "eth:QDC": "1.11.2", + "eth:QKA": "1.11.3", "eth:QKC:100000": "1.10.3", "eth:QKC:100001": "1.10.3", "eth:QKC:100002": "1.10.3", @@ -2217,29 +2313,41 @@ "eth:QKC:100007": "1.10.3", "eth:QKC:100008": "1.10.3", "eth:QKI": "1.10.3", + "eth:QOM": "1.11.3", "eth:RBD": "1.9.4", "eth:RBTC": "1.6.2", + "eth:REAL": "1.11.3", + "eth:REDLC": "1.11.3", "eth:REI:47805": "1.10.6", + "eth:RESIN": "1.11.3", "eth:RING": "1.11.2", "eth:RNA": "1.10.3", "eth:ROC:1288": "1.11.2", - "eth:ROSE": "1.10.5", "eth:RPG": "1.10.5", "eth:RUPX": "1.9.5", + "eth:SAMA": "1.11.3", "eth:SDN": "1.10.4", + "eth:SEED": "1.11.3", "eth:SETM": "1.10.6", "eth:SFL": "1.11.2", "eth:SGB": "1.10.3", "eth:SHIB": "1.10.5", + "eth:SINSO": "1.11.3", "eth:SKU": "1.10.3", "eth:SMT": "1.11.2", "eth:SNT": "1.10.6", "eth:SOTER:68": "1.11.2", "eth:SPARK": "1.10.5", "eth:SPOA": "1.10.5", + "eth:SRDX": "1.11.3", "eth:SRN": "1.10.4", + "eth:STAND": "1.11.3", + "eth:STOS": "1.11.3", + "eth:SVRN": "1.11.3", "eth:SX": "1.11.2", "eth:Seele": "1.10.5", + "eth:SmuX": "1.11.3", + "eth:T-EKTA": "1.11.3", "eth:TAO": "1.9.4", "eth:TBG": "1.9.5", "eth:TCH": "1.9.4", @@ -2249,6 +2357,7 @@ "eth:TFI": "1.9.5", "eth:TLC": "1.11.2", "eth:TLOS:40": "1.10.3", + "eth:TMY": "1.11.3", "eth:TOMB": "1.11.2", "eth:TOMO:88": "1.10.6", "eth:TOYS": "1.9.5", @@ -2258,52 +2367,77 @@ "eth:TST": "1.9.4", "eth:TT": "1.9.4", "eth:TUBQ": "1.9.4", + "eth:TWL": "1.11.3", "eth:TXDC": "1.9.5", "eth:TXL": "1.11.2", "eth:U+25B3": "1.9.4", "eth:UBC": "1.10.6", "eth:UBQ": "1.6.2", + "eth:ULX": "1.11.3", + "eth:UNQ": "1.11.3", "eth:UZMI": "1.10.5", "eth:VAL": "1.9.4", + "eth:VET": "1.11.3", + "eth:VETH": "1.11.3", "eth:VLX": "1.10.4", "eth:VNDT": "1.11.2", + "eth:VNT": "1.11.3", "eth:VS:888888": "1.10.6", + "eth:VSC": "1.11.3", "eth:W3G": "1.10.6", "eth:W3Q:333": "1.10.6", "eth:WAN": "1.10.3", "eth:WEB": "1.9.4", + "eth:WEMIX": "1.11.3", "eth:WGM": "1.10.6", "eth:WTT": "1.10.5", "eth:XDC": "1.9.5", "eth:XERO": "1.9.4", + "eth:XETA": "1.11.3", + "eth:XODEX": "1.11.3", "eth:XT": "1.11.2", "eth:XVM": "1.11.2", + "eth:XZO": "1.11.3", "eth:YCC": "1.11.2", "eth:YETI": "1.9.4", + "eth:ZENIQ": "1.11.3", "eth:ZENITH": "1.11.2", "eth:ZERO": "1.10.5", + "eth:ZETA": "1.11.3", + "eth:ZTH:427": "1.11.3", "eth:ZYX": "1.10.4", "eth:atp": "1.10.5", "eth:cTH": "1.10.3", "eth:cet": "1.10.3", + "eth:eBTC": "1.11.3", + "eth:hP2": "1.11.3", "eth:jfin": "1.11.2", "eth:lat": "1.10.6", "eth:mADA": "1.11.2", + "eth:mALGO": "1.11.3", + "eth:mc": "1.11.3", + "eth:nSAN": "1.11.3", "eth:pCKB": "1.11.2", "eth:peggle": "1.9.5", "eth:tATS": "1.8.0", "eth:tAVAX": "1.9.5", "eth:tBNB": "1.9.4", + "eth:tBVE": "1.11.3", "eth:tCELO:44787": "1.9.5", "eth:tCELO:62320": "1.9.5", "eth:tCFLR": "1.9.5", + "eth:tCRC": "1.11.3", "eth:tDBM": "1.9.4", - "eth:tGOR": "1.11.2", + "eth:tETH:11155111": "1.11.3", + "eth:tETH:3": "1.11.3", + "eth:tETH:4": "1.11.3", + "eth:tETH:42": "1.11.3", + "eth:tETH:5": "1.11.3", "eth:tKAL": "1.9.4", + "eth:tKEK": "1.11.3", "eth:tKLAY": "1.9.5", - "eth:tKOR": "1.9.5", "eth:tKOT": "1.9.4", - "eth:tKOV": "1.6.2", + "eth:tMAP": "1.11.3", "eth:tMATH": "1.9.5", "eth:tMATIC": "1.9.4", "eth:tMETC": "1.9.4", @@ -2311,16 +2445,15 @@ "eth:tNRG": "1.9.4", "eth:tPHT": "1.9.4", "eth:tRBTC": "1.6.2", - "eth:tRIN": "1.6.2", - "eth:tROP": "1.6.2", - "eth:tSEP": "1.10.6", "eth:tVT": "1.9.4", + "eth:tWIRE": "1.11.3", + "eth:taro": "1.11.3", "eth:thtt": "1.9.5", "eth:tmACA": "1.9.5", "eth:tsDIODE": "1.9.5", "eth:xDAI:100": "1.9.4", "eth:xDAI:300": "1.11.2", - "eth:\u03a6": "1.11.2", + "eth:xlon": "1.11.3", "eth:\u25c8": "1.11.2", "misc:MAID": "1.7.2", "misc:OMNI": "1.7.2", @@ -2340,8 +2473,10 @@ "erc20:etc:PLAY": "(AUTO) duplicate key", "erc20:eth:A18:ba7d": "(AUTO) duplicate key", "erc20:eth:A18:bde8": "(AUTO) duplicate key", - "erc20:eth:ANT": "(AUTO) duplicate key", - "erc20:eth:ANT (old)": "(AUTO) duplicate key", + "erc20:eth:ADX:4470": "(AUTO) duplicate key", + "erc20:eth:ADX:ade0": "(AUTO) duplicate key", + "erc20:eth:ANT:960b": "(AUTO) duplicate key", + "erc20:eth:ANT:a117": "(AUTO) duplicate key", "erc20:eth:ATH": "(AUTO) duplicate key", "erc20:eth:ATH (AIgatha Token)": "(AUTO) duplicate key", "erc20:eth:ATS": "duplicate key with eth:ATS", @@ -2399,8 +2534,6 @@ "erc20:eth:CTT:e3fa": "(AUTO) duplicate key", "erc20:eth:DAO:0f51": "(AUTO) duplicate key", "erc20:eth:DAO:bb9b": "(AUTO) duplicate key", - "erc20:eth:DATA:0cf0": "(AUTO) duplicate key", - "erc20:eth:DATA:8f69": "(AUTO) duplicate key", "erc20:eth:DEPO": "(AUTO) duplicate key", "erc20:eth:DEPO (Depository Network)": "(AUTO) duplicate key", "erc20:eth:DGTX:1c83": "(AUTO) duplicate key", @@ -2476,6 +2609,8 @@ "erc20:eth:MDS:92b7": "(AUTO) duplicate key", "erc20:eth:MESH:01f2": "(AUTO) duplicate key", "erc20:eth:MESH:f030": "(AUTO) duplicate key", + "erc20:eth:MET:2ebd": "(AUTO) duplicate key", + "erc20:eth:MET:a3d5": "(AUTO) duplicate key", "erc20:eth:MIT": "(AUTO) duplicate key", "erc20:eth:MIT (Mychatcoin)": "(AUTO) duplicate key", "erc20:eth:MOC": "(AUTO) duplicate key", @@ -2597,31 +2732,72 @@ "eth:ATH:1620": "duplicate key", "eth:ATH:43110": "duplicate key", "eth:BCH": "duplicate of bitcoin:BCH", + "eth:BOBA:1294": "duplicate key", + "eth:BOBA:301": "duplicate key", + "eth:BOBA:43288": "duplicate key", + "eth:BOBA:56288": "duplicate key", + "eth:BOBA:97288": "duplicate key", + "eth:BRO:1039": "duplicate key", + "eth:BRO:108801": "duplicate key", "eth:BTX": "duplicate key", - "eth:CPAY:1337": "deprecated", + "eth:CAM:501": "duplicate key", + "eth:CCN:970": "duplicate key", + "eth:CCN:9700": "duplicate key", + "eth:CCN:971": "duplicate key", + "eth:DOGE": "duplicate key", "eth:ELA:22": "duplicate of ELA-ETH-sidechain", "eth:ELLA:64": "duplicate symbol", "eth:ELLA:7027": "duplicate symbol", "eth:EOS": "mainnet exists", "eth:ETH:10": "duplicate key", + "eth:ETH:10086": "duplicate key", + "eth:ETH:116": "duplicate key", "eth:ETH:1313161554": "duplicate key", "eth:ETH:1313161556": "duplicate key", "eth:ETH:1337802": "eth collision", + "eth:ETH:1337803": "duplicate key", "eth:ETH:288": "duplicate key", + "eth:ETH:324": "duplicate key", "eth:ETH:42161": "duplicate key", "eth:ETH:42170": "eth collision", + "eth:ETH:50001": "duplicate key", + "eth:ETH:534352": "duplicate key", "eth:ETH:5551": "eth collision", "eth:ETH:73927": "duplicate key", + "eth:ETH:8453": "duplicate key", "eth:ETH:980": "eth collision", + "eth:FIRE:5290": "duplicate key", + "eth:FRA:2152": "duplicate key", + "eth:FRA:2154": "duplicate key", "eth:GAR:91": "duplicate key", "eth:GAR:92": "duplicate key", "eth:GAR:93": "duplicate key", + "eth:GT:10024": "duplicate key", + "eth:GT:86": "duplicate key", "eth:GTH:192837465": "duplicate key", "eth:GTH:486217935": "exclude testnet", + "eth:HBAR:297": "duplicate key", + "eth:HBAR:298": "duplicate key", "eth:JEWEL:335": "exclude testnet", "eth:KTO:8285": "exclude testnet", + "eth:MCD:217": "duplicate key", + "eth:MCD:67390": "duplicate key", + "eth:METAL:381931": "duplicate key", + "eth:METAL:381932": "duplicate key", "eth:NEON:245022926": "duplicate key", + "eth:OAS:19011": "duplicate key", + "eth:OAS:2400": "duplicate key", + "eth:OAS:248": "duplicate key", + "eth:OAS:5555": "duplicate key", + "eth:OAS:7225878": "duplicate key", + "eth:OAS:876": "duplicate key", + "eth:ONE:1666900000": "duplicate key", "eth:PFT:808": "testnet", + "eth:PI:2099156": "duplicate key", + "eth:PI:8007736": "duplicate key", + "eth:POSI:900000": "duplicate key", + "eth:POSI:920000": "duplicate key", + "eth:POSI:920001": "duplicate key", "eth:Q": "causing problems in altcoin detection", "eth:QKC:110000": "exclude testnet", "eth:QKC:110001": "exclude testnet", @@ -2632,14 +2808,28 @@ "eth:QKC:110006": "exclude testnet", "eth:QKC:110007": "exclude testnet", "eth:QKC:110008": "exclude testnet", + "eth:QTZ:8881": "duplicate key", + "eth:QTZ:8883": "duplicate key", "eth:REI:55555": "duplicate key", "eth:ROC:1286": "deprecated", + "eth:ROSE:23294": "duplicate key", + "eth:ROSE:42262": "duplicate key", + "eth:SHM:8080": "duplicate key", + "eth:SHM:8081": "duplicate key", + "eth:SHM:8082": "duplicate key", "eth:SHYFT": "slip44 out of range", "eth:SOTER:218": "deprecated", "eth:SYS": "duplicate key", + "eth:T-FSN": "(AUTO) exclude testnet", + "eth:TARA:841": "duplicate key", + "eth:TARA:842": "(AUTO) exclude testnet", "eth:TAero": "(AUTO) exclude testnet", + "eth:TBITCI": "(AUTO) exclude testnet", + "eth:TBXN": "(AUTO) exclude testnet", "eth:TCRO": "(AUTO) exclude testnet", "eth:TELE:8001": "(AUTO) exclude testnet", + "eth:TEST": "(AUTO) exclude testnet", + "eth:TEXL": "(AUTO) exclude testnet", "eth:TFUEL:361": "incompatible", "eth:TFUEL:363": "(AUTO) exclude testnet", "eth:TFUEL:364": "(AUTO) exclude testnet", @@ -2655,56 +2845,128 @@ "eth:TKM:70103": "duplicate key", "eth:TLA": "(AUTO) exclude testnet", "eth:TLOS:41": "(AUTO) exclude testnet", + "eth:TOKI:8654": "duplicate key", + "eth:TOKI:8655": "(AUTO) exclude testnet", "eth:TOMO:89": "(AUTO) exclude testnet", "eth:TOP": "causing problems in altcoin detection", "eth:TOPC": "(AUTO) exclude testnet", + "eth:TPBX:404040": "duplicate key", + "eth:TPBX:4141": "(AUTO) exclude testnet", + "eth:TPI:10067275": "(AUTO) exclude testnet", + "eth:TPI:16658437": "(AUTO) exclude testnet", + "eth:TRES:6065": "(AUTO) exclude testnet", + "eth:TRES:6066": "duplicate key", "eth:TSCAS": "(AUTO) exclude testnet", + "eth:TSETH": "(AUTO) exclude testnet", + "eth:USD:2018": "duplicate key", + "eth:USD:2020": "duplicate key", "eth:VS:666666": "exclude testnet", "eth:W3Q:3334": "duplicate key", + "eth:ZTH:859": "duplicate key", + "eth:sFUEL:1482601649": "duplicate key", + "eth:sFUEL:1564830818": "duplicate key", + "eth:sFUEL:2046399126": "duplicate key", + "eth:sFUEL:503129905": "duplicate key", + "eth:t$BNI": "(AUTO) exclude testnet", "eth:tAAC": "(AUTO) exclude testnet", "eth:tACA": "(AUTO) exclude testnet", "eth:tAGOR": "testnet", "eth:tAIOZ": "(AUTO) exclude testnet", "eth:tAITD": "(AUTO) exclude testnet", "eth:tALOT": "(AUTO) exclude testnet", - "eth:tAMBROS": "(AUTO) exclude testnet", - "eth:tARETH": "(AUTO) exclude testnet", + "eth:tALYX": "(AUTO) exclude testnet", + "eth:tAMAX": "(AUTO) exclude testnet", + "eth:tAMB": "(AUTO) exclude testnet", + "eth:tASA": "(AUTO) exclude testnet", + "eth:tATN:65010000": "(AUTO) exclude testnet", + "eth:tATN:65100000": "(AUTO) exclude testnet", + "eth:tATOS": "(AUTO) exclude testnet", "eth:tAVIS": "(AUTO) exclude testnet", + "eth:tAcol": "(AUTO) exclude testnet", "eth:tBCHT": "(AUTO) exclude testnet", "eth:tBCS": "(AUTO) exclude testnet", "eth:tBELLY": "(AUTO) exclude testnet", - "eth:tBOBA": "(AUTO) exclude testnet", + "eth:tBFC": "(AUTO) exclude testnet", + "eth:tBIT": "(AUTO) exclude testnet", + "eth:tBLG": "(AUTO) exclude testnet", + "eth:tBOBA:1297": "(AUTO) exclude testnet", + "eth:tBOBA:4051": "(AUTO) exclude testnet", + "eth:tBOBA:4328": "(AUTO) exclude testnet", + "eth:tBOBA:9728": "(AUTO) exclude testnet", + "eth:tBOC": "(AUTO) exclude testnet", + "eth:tBOMB": "(AUTO) exclude testnet", + "eth:tBRNKC": "(AUTO) exclude testnet", + "eth:tBRO": "(AUTO) exclude testnet", "eth:tBTM": "(AUTO) exclude testnet", "eth:tBTT": "(AUTO) exclude testnet", + "eth:tC2FLR": "(AUTO) exclude testnet", + "eth:tCANTO": "(AUTO) exclude testnet", "eth:tCFX": "(AUTO) exclude testnet", + "eth:tCHZ": "(AUTO) exclude testnet", + "eth:tCICT": "(AUTO) exclude testnet", "eth:tCKB:71393": "(AUTO) exclude testnet", "eth:tCKB:868455272153094": "(AUTO) exclude testnet", + "eth:tCLD": "(AUTO) exclude testnet", + "eth:tCLO": "(AUTO) exclude testnet", "eth:tCLV": "(AUTO) exclude testnet", "eth:tCMP": "(AUTO) exclude testnet", + "eth:tCNT": "(AUTO) exclude testnet", + "eth:tCORE": "(AUTO) exclude testnet", "eth:tCTE": "testnet", "eth:tCUBET": "(AUTO) exclude testnet", "eth:tCWN": "(AUTO) exclude testnet", + "eth:tDB": "(AUTO) exclude testnet", + "eth:tDEL": "(AUTO) exclude testnet", + "eth:tDFI": "(AUTO) exclude testnet", + "eth:tDOGE": "(AUTO) exclude testnet", + "eth:tDOGS": "(AUTO) exclude testnet", "eth:tDTH": "(AUTO) exclude testnet", "eth:tDX": "(AUTO) exclude testnet", "eth:tDYNO": "(AUTO) exclude testnet", "eth:tECE": "(AUTO) exclude testnet", + "eth:tECG": "(AUTO) exclude testnet", "eth:tECO": "(AUTO) exclude testnet", "eth:tECS": "(AUTO) exclude testnet", "eth:tEDG": "(AUTO) exclude testnet", + "eth:tEDX": "(AUTO) exclude testnet", "eth:tELA:21": "(AUTO) exclude testnet", "eth:tELA:23": "(AUTO) exclude testnet", "eth:tEOS": "(AUTO) exclude testnet", + "eth:tETH:115": "(AUTO) exclude testnet", "eth:tETH:1313161555": "(AUTO) exclude testnet", + "eth:tETH:1337": "(AUTO) exclude testnet", + "eth:tETH:1402": "(AUTO) exclude testnet", + "eth:tETH:1422": "(AUTO) exclude testnet", "eth:tETH:28": "(AUTO) exclude testnet", "eth:tETH:280": "(AUTO) exclude testnet", + "eth:tETH:28528": "(AUTO) exclude testnet", + "eth:tETH:2888": "(AUTO) exclude testnet", "eth:tETH:420": "(AUTO) exclude testnet", + "eth:tETH:421611": "duplicate key", + "eth:tETH:534353": "(AUTO) exclude testnet", "eth:tETH:5553": "(AUTO) exclude testnet", + "eth:tETH:5777": "duplicate key", + "eth:tETH:69": "duplicate key", + "eth:tETH:84531": "(AUTO) exclude testnet", + "eth:tETH:956": "(AUTO) exclude testnet", + "eth:tETMP": "(AUTO) exclude testnet", "eth:tEUN": "(AUTO) exclude testnet", + "eth:tEVM": "(AUTO) exclude testnet", "eth:tEVMOS": "(AUTO) exclude testnet", "eth:tEZC": "(AUTO) exclude testnet", - "eth:tFETH": "(AUTO) exclude testnet", + "eth:tFIL:3141": "(AUTO) exclude testnet", + "eth:tFIL:31415": "(AUTO) exclude testnet", + "eth:tFIL:314159": "(AUTO) exclude testnet", + "eth:tFIL:3141592": "(AUTO) exclude testnet", + "eth:tFIL:31415926": "(AUTO) exclude testnet", + "eth:tFIRE:917": "(AUTO) exclude testnet", + "eth:tFIRE:9170": "(AUTO) exclude testnet", + "eth:tFITFI": "(AUTO) exclude testnet", + "eth:tFNCY": "(AUTO) exclude testnet", "eth:tFRA": "(AUTO) exclude testnet", "eth:tFTM": "(AUTO) exclude testnet", + "eth:tFTN": "(AUTO) exclude testnet", + "eth:tFtREN": "(AUTO) exclude testnet", "eth:tGAR:900": "(AUTO) exclude testnet", "eth:tGAR:901": "(AUTO) exclude testnet", "eth:tGAR:902": "(AUTO) exclude testnet", @@ -2712,20 +2974,34 @@ "eth:tGCD": "(AUTO) exclude testnet", "eth:tGO": "(AUTO) exclude testnet", "eth:tGT": "(AUTO) exclude testnet", + "eth:tGTFX": "(AUTO) exclude testnet", "eth:tGTH": "(AUTO) exclude testnet", + "eth:tGZN": "(AUTO) exclude testnet", "eth:tGooD": "(AUTO) exclude testnet", + "eth:tHBAR": "(AUTO) exclude testnet", + "eth:tHIK": "(AUTO) exclude testnet", "eth:tHOO": "(AUTO) exclude testnet", + "eth:tHPN": "(AUTO) exclude testnet", "eth:tHYM": "(AUTO) exclude testnet", + "eth:tIDE": "(AUTO) exclude testnet", + "eth:tIMV": "(AUTO) exclude testnet", "eth:tIOTX": "(AUTO) exclude testnet", + "eth:tISLMT": "(AUTO) exclude testnet", "eth:tIVAR": "(AUTO) exclude testnet", + "eth:tJINDA": "(AUTO) exclude testnet", "eth:tKAIBA": "(AUTO) exclude testnet", "eth:tKAR": "(AUTO) exclude testnet", "eth:tKCS": "(AUTO) exclude testnet", "eth:tKSX": "(AUTO) exclude testnet", + "eth:tKUB": "(AUTO) exclude testnet", "eth:tL1test": "(AUTO) exclude testnet", + "eth:tLAMB": "(AUTO) exclude testnet", + "eth:tLAVA": "(AUTO) exclude testnet", "eth:tMDGLT": "(AUTO) exclude testnet", + "eth:tMEER-T": "(AUTO) exclude testnet", "eth:tMESHT": "(AUTO) exclude testnet", - "eth:tMETIS": "(AUTO) exclude testnet", + "eth:tMETIS:588": "(AUTO) exclude testnet", + "eth:tMETIS:599": "(AUTO) exclude testnet", "eth:tMTR": "(AUTO) exclude testnet", "eth:tMTTest": "(AUTO) exclude testnet", "eth:tMYN": "(AUTO) exclude testnet", @@ -2733,6 +3009,10 @@ "eth:tNEON": "(AUTO) exclude testnet", "eth:tNMAC": "(AUTO) exclude testnet", "eth:tNTTH": "(AUTO) exclude testnet", + "eth:tNULS": "(AUTO) exclude testnet", + "eth:tNUM": "(AUTO) exclude testnet", + "eth:tNetZ": "(AUTO) exclude testnet", + "eth:tOAS": "(AUTO) exclude testnet", "eth:tOKT": "(AUTO) exclude testnet", "eth:tOLO": "(AUTO) exclude testnet", "eth:tOLT": "(AUTO) exclude testnet", @@ -2741,6 +3021,8 @@ "eth:tONE:1666700002": "(AUTO) exclude testnet", "eth:tONE:1666700003": "(AUTO) exclude testnet", "eth:tONG": "(AUTO) exclude testnet", + "eth:tONUS": "(AUTO) exclude testnet", + "eth:tOONE": "(AUTO) exclude testnet", "eth:tORING": "(AUTO) exclude testnet", "eth:tOY": "(AUTO) exclude testnet", "eth:tPALM": "(AUTO) exclude testnet", @@ -2749,38 +3031,66 @@ "eth:tPLS:941": "(AUTO) exclude testnet", "eth:tPLS:942": "(AUTO) exclude testnet", "eth:tPOLIS": "(AUTO) exclude testnet", + "eth:tPOSI": "(AUTO) exclude testnet", "eth:tPRB": "(AUTO) exclude testnet", "eth:tPRING": "(AUTO) exclude testnet", + "eth:tPRX": "(AUTO) exclude testnet", + "eth:tPTX": "(AUTO) exclude testnet", "eth:tQ": "causing problems in altcoin detection", "eth:tQDC": "(AUTO) exclude testnet", + "eth:tQET": "(AUTO) exclude testnet", + "eth:tQOM": "(AUTO) exclude testnet", "eth:tREI": "(AUTO) exclude testnet", - "eth:tRNA": "(AUTO) exclude testnet", "eth:tROSE": "(AUTO) exclude testnet", "eth:tRPG": "(AUTO) exclude testnet", "eth:tSFL": "(AUTO) exclude testnet", + "eth:tSHT": "(AUTO) exclude testnet", "eth:tSHYFTT": "(AUTO) exclude testnet", + "eth:tSINSO": "(AUTO) exclude testnet", "eth:tSNS": "(AUTO) exclude testnet", + "eth:tSRDX": "(AUTO) exclude testnet", + "eth:tSTAND": "(AUTO) exclude testnet", + "eth:tSTOS": "(AUTO) exclude testnet", + "eth:tSVRN": "(AUTO) exclude testnet", "eth:tSX": "(AUTO) exclude testnet", "eth:tSYS": "(AUTO) exclude testnet", + "eth:tU2U": "(AUTO) exclude testnet", "eth:tUBC": "(AUTO) exclude testnet", + "eth:tULX": "(AUTO) exclude testnet", + "eth:tUNQ": "(AUTO) exclude testnet", + "eth:tUSD": "(AUTO) exclude testnet", + "eth:tVET": "(AUTO) exclude testnet", "eth:tVIL": "(AUTO) exclude testnet", + "eth:tVNT": "(AUTO) exclude testnet", "eth:tW3G": "(AUTO) exclude testnet", "eth:tW3Q": "(AUTO) exclude testnet", "eth:tWAN": "(AUTO) exclude testnet", + "eth:tWEMIX": "(AUTO) exclude testnet", "eth:tWLK": "(AUTO) exclude testnet", "eth:tXPR": "(AUTO) exclude testnet", "eth:tXVM": "(AUTO) exclude testnet", "eth:tZCR": "(AUTO) exclude testnet", + "eth:tZEN": "(AUTO) exclude testnet", "eth:tZERO": "(AUTO) exclude testnet", + "eth:tZKST": "(AUTO) exclude testnet", + "eth:taZETA": "(AUTO) exclude testnet", "eth:tatp": "(AUTO) exclude testnet", "eth:tcett": "(AUTO) exclude testnet", "eth:tkiETH": "(AUTO) exclude testnet", "eth:tlat:2203181": "(AUTO) exclude testnet", "eth:tlat:2206132": "(AUTO) exclude testnet", "eth:tmTAda": "(AUTO) exclude testnet", + "eth:tmTAlgo": "(AUTO) exclude testnet", + "eth:tmc": "(AUTO) exclude testnet", "eth:tpCKB": "(AUTO) exclude testnet", + "eth:trAna": "(AUTO) exclude testnet", + "eth:tsFUEL": "(AUTO) exclude testnet", "eth:tusd": "(AUTO) exclude testnet", + "eth:txDAI:100100": "(AUTO) exclude testnet", + "eth:txDAI:10200": "(AUTO) exclude testnet", "eth:xDAI:200": "duplicate key", + "eth:\u03a6:144": "duplicate key", + "eth:\u03a6:4181": "duplicate key", "misc:ADA": "not implemented", "misc:BNB": "not implemented", "misc:EOS": "not implemented", @@ -2849,8 +3159,8 @@ "bitcoin:tQTUM": "2.1.1", "bitcoin:tRVN": "2.4.0", "bitcoin:tSMART": "2.0.8", - "erc20:avax:AVAX": "2.5.2", "erc20:avax:USDT": "2.5.2", + "erc20:avax:WAVAX": "2.5.4", "erc20:esn:DGT": "2.0.8", "erc20:esn:TOPM": "2.0.8", "erc20:etc:BEC": "2.0.7", @@ -2886,9 +3196,7 @@ "erc20:eth:ADL": "2.0.7", "erc20:eth:ADST": "2.0.7", "erc20:eth:ADT": "2.0.7", - "erc20:eth:ADX": "2.0.7", "erc20:eth:ADX-LOYALTY": "2.3.7", - "erc20:eth:ADXL": "2.3.7", "erc20:eth:AE": "2.0.7", "erc20:eth:AEUR": "2.3.0", "erc20:eth:AFA": "2.0.10", @@ -3177,6 +3485,7 @@ "erc20:eth:DALC": "2.0.7", "erc20:eth:DAN": "2.0.7", "erc20:eth:DAT": "2.0.7", + "erc20:eth:DATA": "2.5.4", "erc20:eth:DATABroker": "2.0.7", "erc20:eth:DATX": "2.0.11", "erc20:eth:DAV": "2.0.7", @@ -3389,6 +3698,7 @@ "erc20:eth:GIF": "2.0.7", "erc20:eth:GIM": "2.0.7", "erc20:eth:GIRL": "2.1.8", + "erc20:eth:GIV": "2.5.4", "erc20:eth:GL": "2.3.1", "erc20:eth:GLA": "2.0.10", "erc20:eth:GMB": "2.5.2", @@ -3410,6 +3720,7 @@ "erc20:eth:GSE": "2.0.8", "erc20:eth:GST2": "2.3.0", "erc20:eth:GTC": "2.0.7", + "erc20:eth:GTEC": "2.5.4", "erc20:eth:GTH": "2.5.2", "erc20:eth:GTKT": "2.0.7", "erc20:eth:GTO": "2.0.7", @@ -3577,7 +3888,7 @@ "erc20:eth:LOOK": "2.0.8", "erc20:eth:LOOKS": "2.5.2", "erc20:eth:LOOM": "2.0.7", - "erc20:eth:LOOMOLD": "2.4.2", + "erc20:eth:LOOM:deprecated": "2.5.4", "erc20:eth:LOVE": "2.0.8", "erc20:eth:LPT": "2.0.7", "erc20:eth:LQD": "2.3.0", @@ -3612,7 +3923,6 @@ "erc20:eth:MEME": "2.3.7", "erc20:eth:MESG": "2.1.8", "erc20:eth:MEST": "2.0.7", - "erc20:eth:MET": "2.0.10", "erc20:eth:METM": "2.0.10", "erc20:eth:MFG": "2.0.7", "erc20:eth:MFT": "2.0.8", @@ -3760,7 +4070,6 @@ "erc20:eth:PATENTS": "2.0.7", "erc20:eth:PATH": "2.0.7", "erc20:eth:PATR": "2.3.0", - "erc20:eth:PAX": "2.0.10", "erc20:eth:PAXG": "2.1.8", "erc20:eth:PAY": "2.0.7", "erc20:eth:PBL": "2.0.7", @@ -3959,8 +4268,7 @@ "erc20:eth:SNM": "2.0.7", "erc20:eth:SNOV": "2.0.7", "erc20:eth:SNT": "2.0.7", - "erc20:eth:SNTR": "2.0.10", - "erc20:eth:SNX:c011": "2.4.2", + "erc20:eth:SNX": "2.5.4", "erc20:eth:SOAR": "2.0.11", "erc20:eth:SOC": "2.0.10", "erc20:eth:SOCKS": "2.3.0", @@ -3984,7 +4292,6 @@ "erc20:eth:SPZ": "2.3.0", "erc20:eth:SRN": "2.0.7", "erc20:eth:SRX": "2.1.8", - "erc20:eth:SSH": "2.0.7", "erc20:eth:SSP": "2.0.10", "erc20:eth:STABIT": "2.0.8", "erc20:eth:STAC": "2.0.7", @@ -4015,6 +4322,7 @@ "erc20:eth:SWASH": "2.4.3", "erc20:eth:SWFTC": "2.0.10", "erc20:eth:SWM": "2.0.7", + "erc20:eth:SWRM": "2.5.4", "erc20:eth:SWT": "2.0.7", "erc20:eth:SXDT": "2.0.7", "erc20:eth:SXR": "2.3.0", @@ -4023,6 +4331,7 @@ "erc20:eth:SYS": "2.4.4", "erc20:eth:SYSX": "2.3.7", "erc20:eth:SenSatorI": "2.0.7", + "erc20:eth:Skey": "2.5.4", "erc20:eth:TALAO": "2.0.10", "erc20:eth:TAN": "2.3.0", "erc20:eth:TAU": "2.0.7", @@ -4040,6 +4349,7 @@ "erc20:eth:TCST": "2.1.8", "erc20:eth:TCT": "2.1.8", "erc20:eth:TDH": "2.0.7", + "erc20:eth:TDX": "2.5.4", "erc20:eth:TEAM": "2.0.10", "erc20:eth:TECH": "2.3.0", "erc20:eth:TEN": "2.0.10", @@ -4080,6 +4390,7 @@ "erc20:eth:TRCT": "2.0.10", "erc20:eth:TRDT": "2.0.10", "erc20:eth:TRST": "2.0.7", + "erc20:eth:TRUCCO": "2.5.4", "erc20:eth:TRYB": "2.3.0", "erc20:eth:TSW": "2.0.8", "erc20:eth:TTA": "2.0.10", @@ -4110,6 +4421,7 @@ "erc20:eth:USD-G": "2.3.0", "erc20:eth:USDC": "2.0.8", "erc20:eth:USDM": "2.0.7", + "erc20:eth:USDP": "2.5.4", "erc20:eth:USDS": "2.0.11", "erc20:eth:USDT": "2.0.10", "erc20:eth:USDx": "2.3.0", @@ -4168,6 +4480,7 @@ "erc20:eth:WCK": "2.3.0", "erc20:eth:WCN": "2.0.7", "erc20:eth:WCT": "2.0.7", + "erc20:eth:WDOGE": "2.5.4", "erc20:eth:WEB": "2.0.10", "erc20:eth:WETH": "2.0.7", "erc20:eth:WHEN": "2.0.7", @@ -4202,6 +4515,7 @@ "erc20:eth:XCHF": "2.0.11", "erc20:eth:XCL": "2.3.0", "erc20:eth:XCLR": "2.0.10", + "erc20:eth:XDATA": "2.5.4", "erc20:eth:XDCE": "2.0.10", "erc20:eth:XES": "2.0.10", "erc20:eth:XET": "2.0.10", @@ -4328,6 +4642,7 @@ "erc20:eth:eosDAC": "2.0.7", "erc20:eth:fstETHDAI": "2.3.1", "erc20:eth:iBAT": "2.3.0", + "erc20:eth:iBBT": "2.5.4", "erc20:eth:iBNB": "2.3.0", "erc20:eth:iBTC": "2.3.0", "erc20:eth:iCEX": "2.3.0", @@ -4686,8 +5001,8 @@ "erc20:rop:RLC": "2.5.2", "erc20:rop:dqr30": "2.0.10", "erc20:rsk:ARSCB": "2.4.3", + "erc20:rsk:BITP": "2.5.4", "erc20:rsk:BOBCB": "2.4.3", - "erc20:rsk:BPro": "2.4.3", "erc20:rsk:BRLCB": "2.4.3", "erc20:rsk:BRZ": "2.4.3", "erc20:rsk:BTCCB": "2.4.3", @@ -4736,88 +5051,142 @@ "erc20:ubq:SPHR": "2.4.4", "erc20:ubq:SPHRC": "2.4.4", "erc20:ubq:TGE1": "2.4.4", + "eth:$BNI": "2.5.4", "eth:$OC": "2.4.4", + "eth:0XT": "2.5.4", "eth:0xF": "2.3.5", - "eth:420": "2.3.7", "eth:AAC": "2.4.4", "eth:ACA": "2.3.7", + "eth:ACE": "2.5.4", "eth:AIOZ": "2.5.2", "eth:AITD": "2.5.2", "eth:AKA": "2.0.8", + "eth:ALOT": "2.5.4", + "eth:ALPH": "2.5.4", + "eth:ALT": "2.5.4", + "eth:ALYX": "2.5.4", + "eth:AMAX": "2.5.4", + "eth:AMB": "2.5.4", "eth:AMBROS": "2.5.2", "eth:AME": "2.5.2", + "eth:ANY": "2.5.4", "eth:APTA": "2.5.3", "eth:AQUA": "2.1.1", + "eth:AR\u00c9V": "2.5.4", + "eth:ASA": "2.5.4", "eth:ASK": "2.4.3", "eth:ASTR": "2.5.2", + "eth:ATLR": "2.5.4", "eth:ATS": "2.0.11", "eth:AUX": "2.1.6", "eth:AVAX": "2.3.7", + "eth:AVS": "2.5.4", "eth:BCS": "2.4.4", "eth:BELLY": "2.5.2", + "eth:BFC": "2.5.4", + "eth:BG": "2.5.4", + "eth:BIT": "2.5.4", + "eth:BITCI": "2.5.4", "eth:BNB": "2.3.5", - "eth:BOBA": "2.5.2", + "eth:BOA": "2.5.4", + "eth:BOMB": "2.5.4", "eth:BOY": "2.4.4", "eth:BRB": "2.5.2", - "eth:BRO": "2.4.4", + "eth:BRNKC": "2.5.4", "eth:BTA": "2.4.3", "eth:BTCIX": "2.4.4", "eth:BTM": "2.4.4", + "eth:BTON": "2.5.4", "eth:BTT": "2.4.3", + "eth:BTY": "2.5.4", + "eth:BXN": "2.5.4", "eth:Brise": "2.5.2", + "eth:CAM:500": "2.5.4", + "eth:CANTO": "2.5.4", "eth:CATE": "2.4.2", + "eth:CCNA": "2.5.4", "eth:CCP": "2.4.4", "eth:CELO": "2.3.7", "eth:CEM": "2.5.2", "eth:CFX": "2.4.4", + "eth:CIC": "2.5.4", "eth:CLASS": "2.4.4", + "eth:CLD": "2.5.4", "eth:CLO": "2.0.7", "eth:CLV": "2.4.2", + "eth:CMEMO": "2.5.4", + "eth:CMP": "2.5.4", "eth:CNDL": "2.5.2", + "eth:CONDOR": "2.5.4", + "eth:CORE": "2.5.4", "eth:CPAY:21337": "2.5.2", "eth:CPAY:3000": "2.5.2", "eth:CPAY:3001": "2.5.2", "eth:CRAB": "2.4.4", + "eth:CRC": "2.5.4", + "eth:CREDIT": "2.5.4", "eth:CRO": "2.4.4", "eth:CSB": "2.5.2", + "eth:CTEX": "2.5.4", "eth:CUBE": "2.5.2", "eth:CWN": "2.5.2", "eth:DAX": "2.4.2", + "eth:DEB": "2.5.4", + "eth:DEL": "2.5.4", "eth:DEV": "2.4.3", - "eth:DGCC": "2.5.2", + "eth:DFI": "2.5.4", "eth:DIODE": "2.3.7", - "eth:DTH": "2.4.4", + "eth:DKN": "2.5.4", + "eth:DOGS": "2.5.4", + "eth:DOINX": "2.5.4", + "eth:DOS": "2.5.4", + "eth:DRAC": "2.5.4", "eth:DWU": "2.4.2", "eth:DX": "2.5.2", + "eth:DXT": "2.5.4", "eth:DYNO": "2.4.4", + "eth:Deh": "2.5.4", + "eth:ECG": "2.5.4", "eth:ECO": "2.4.3", "eth:ECS": "2.4.4", "eth:EDG": "2.4.2", + "eth:EGAZ": "2.5.4", "eth:EGEM": "2.0.7", "eth:EIDI": "2.4.4", + "eth:EKTA": "2.5.4", "eth:ELA:20": "2.4.2", "eth:ELV": "2.4.3", + "eth:EMPIRE": "2.5.4", "eth:ENTER": "2.4.4", "eth:ES": "2.4.2", "eth:ESN": "2.0.8", "eth:ETC": "2.0.7", "eth:ETH:1": "2.0.7", + "eth:ETHF": "2.5.4", "eth:ETHO": "2.0.8", "eth:ETI": "2.1.1", "eth:ETL": "2.4.2", + "eth:ETMP": "2.5.4", "eth:ETND": "2.5.2", "eth:EUN": "2.5.2", "eth:EVA": "2.4.4", "eth:EVC": "2.4.2", "eth:EVMOS": "2.4.4", "eth:EWT": "2.3.5", + "eth:EXL": "2.5.4", "eth:EXP": "2.0.7", "eth:EZC": "2.5.2", "eth:FETH": "2.4.2", + "eth:FIL": "2.5.4", "eth:FIN": "2.3.5", + "eth:FIRE:529": "2.5.4", + "eth:FITFI": "2.5.4", + "eth:FLA": "2.5.4", "eth:FLR": "2.3.7", - "eth:FRA": "2.5.2", + "eth:FNCY": "2.5.4", + "eth:FREN": "2.5.4", "eth:FSN": "2.4.2", + "eth:FST": "2.5.4", "eth:FTM": "2.3.5", "eth:FUSE": "2.3.5", "eth:FX": "2.5.2", @@ -4825,37 +5194,61 @@ "eth:GCD": "2.5.2", "eth:GEN": "2.4.2", "eth:GLMR": "2.4.2", + "eth:GLQ": "2.5.4", + "eth:GMMT": "2.5.4", "eth:GNC": "2.4.4", "eth:GO": "2.0.7", - "eth:GT": "2.4.2", + "eth:GOLDT": "2.5.4", + "eth:GZN": "2.5.4", "eth:GooD": "2.4.2", "eth:HAIC": "2.4.2", + "eth:HBAR:295": "2.5.4", + "eth:HMND": "2.5.4", "eth:HO": "2.4.4", "eth:HOO": "2.5.2", "eth:HOP": "2.4.4", "eth:HPB": "2.1.1", "eth:HT": "2.3.7", + "eth:HTML": "2.5.4", + "eth:HTZ": "2.5.4", "eth:ILT": "2.3.5", + "eth:IMV": "2.5.4", "eth:IORA": "2.4.4", "eth:IOTX": "2.4.2", "eth:IPOS": "2.3.5", + "eth:ISLM": "2.5.4", "eth:IVAR": "2.5.2", + "eth:J": "2.5.4", + "eth:JBC": "2.5.4", "eth:JEWEL:53935": "2.4.4", + "eth:JINDA": "2.5.4", "eth:JOYS": "2.3.7", + "eth:KAI": "2.5.4", "eth:KAR": "2.3.7", "eth:KAVA": "2.5.2", "eth:KCS": "2.4.2", + "eth:KEK": "2.5.4", "eth:KLAY": "2.3.7", "eth:KSX": "2.5.2", "eth:KTO:2559": "2.4.2", + "eth:KUB": "2.5.4", "eth:L1": "2.4.4", "eth:L99": "2.4.3", "eth:LA": "2.4.4", + "eth:LAVA": "2.5.4", "eth:LISINS": "2.5.2", + "eth:LUCID": "2.5.4", "eth:LUDAN": "2.5.2", + "eth:LYC": "2.5.4", + "eth:MAI": "2.5.4", + "eth:MAP": "2.5.4", + "eth:MARO": "2.5.4", + "eth:MAS": "2.5.4", "eth:MATH": "2.3.7", "eth:MATIC": "2.3.5", + "eth:MEER": "2.5.4", "eth:META": "2.1.1", + "eth:METAD": "2.5.4", "eth:METIS": "2.4.4", "eth:MINTME": "2.4.2", "eth:MIX": "2.0.10", @@ -4866,37 +5259,54 @@ "eth:MTT": "2.4.2", "eth:MTV": "2.5.2", "eth:MUSIC": "2.0.8", + "eth:MYTH": "2.5.4", "eth:NEON:245022934": "2.4.3", "eth:NEW": "2.3.7", - "eth:NEXT": "2.4.4", "eth:NRG": "2.3.5", "eth:NTT": "2.4.4", + "eth:NULS": "2.5.4", + "eth:NUM": "2.5.4", + "eth:NetZ": "2.5.4", "eth:OAC": "2.5.2", + "eth:OCTA": "2.5.4", + "eth:OHO": "2.5.4", "eth:OKT": "2.3.7", "eth:OLO": "2.4.2", "eth:OLT": "2.4.3", "eth:OM": "2.5.2", + "eth:OMAX": "2.5.4", "eth:OMC": "2.5.2", "eth:ONE:1666600000": "2.3.7", "eth:ONE:1666600001": "2.3.7", "eth:ONE:1666600002": "2.3.7", "eth:ONE:1666600003": "2.3.7", "eth:ONG": "2.4.2", + "eth:ONUS": "2.5.4", "eth:OPC": "2.5.2", + "eth:OPN": "2.5.4", + "eth:ORL": "2.5.4", + "eth:OTP": "2.5.4", + "eth:OXYN": "2.5.4", "eth:OY": "2.4.4", "eth:PALM": "2.4.3", + "eth:PDC": "2.5.4", "eth:PETH": "2.4.2", "eth:PFT:909": "2.5.2", "eth:PHT": "2.3.5", "eth:PHX": "2.4.4", "eth:PIRL": "2.0.8", "eth:PIX": "2.4.4", + "eth:PLQ": "2.5.4", "eth:PLS": "2.4.3", "eth:POA": "2.3.5", "eth:POLIS": "2.4.4", + "eth:POM": "2.5.4", "eth:POP": "2.4.3", "eth:PRB": "2.4.4", + "eth:PSC": "2.5.4", + "eth:PTX": "2.5.4", "eth:QDC": "2.5.2", + "eth:QKA": "2.5.4", "eth:QKC:100000": "2.4.2", "eth:QKC:100001": "2.4.2", "eth:QKC:100002": "2.4.2", @@ -4907,29 +5317,41 @@ "eth:QKC:100007": "2.4.2", "eth:QKC:100008": "2.4.2", "eth:QKI": "2.4.2", + "eth:QOM": "2.5.4", "eth:RBD": "2.3.5", "eth:RBTC": "2.0.7", + "eth:REAL": "2.5.4", + "eth:REDLC": "2.5.4", "eth:REI:47805": "2.4.4", + "eth:RESIN": "2.5.4", "eth:RING": "2.5.2", "eth:RNA": "2.4.2", "eth:ROC:1288": "2.5.2", - "eth:ROSE": "2.4.4", "eth:RPG": "2.4.4", "eth:RUPX": "2.3.7", + "eth:SAMA": "2.5.4", "eth:SDN": "2.4.3", + "eth:SEED": "2.5.4", "eth:SETM": "2.4.4", "eth:SFL": "2.5.2", "eth:SGB": "2.4.2", "eth:SHIB": "2.4.4", + "eth:SINSO": "2.5.4", "eth:SKU": "2.4.2", "eth:SMT": "2.5.2", "eth:SNT": "2.4.4", "eth:SOTER:68": "2.5.2", "eth:SPARK": "2.4.4", "eth:SPOA": "2.4.4", + "eth:SRDX": "2.5.4", "eth:SRN": "2.4.3", + "eth:STAND": "2.5.4", + "eth:STOS": "2.5.4", + "eth:SVRN": "2.5.4", "eth:SX": "2.5.2", "eth:Seele": "2.4.4", + "eth:SmuX": "2.5.4", + "eth:T-EKTA": "2.5.4", "eth:TAO": "2.3.5", "eth:TBG": "2.3.7", "eth:TCH": "2.3.5", @@ -4939,6 +5361,7 @@ "eth:TFI": "2.3.7", "eth:TLC": "2.5.2", "eth:TLOS:40": "2.4.2", + "eth:TMY": "2.5.4", "eth:TOMB": "2.5.2", "eth:TOMO:88": "2.4.4", "eth:TOYS": "2.3.7", @@ -4948,52 +5371,77 @@ "eth:TST": "2.3.5", "eth:TT": "2.3.5", "eth:TUBQ": "2.3.5", + "eth:TWL": "2.5.4", "eth:TXDC": "2.3.7", "eth:TXL": "2.5.2", "eth:U+25B3": "2.3.5", "eth:UBC": "2.4.4", "eth:UBQ": "2.0.7", + "eth:ULX": "2.5.4", + "eth:UNQ": "2.5.4", "eth:UZMI": "2.4.4", "eth:VAL": "2.3.5", + "eth:VET": "2.5.4", + "eth:VETH": "2.5.4", "eth:VLX": "2.4.3", "eth:VNDT": "2.5.2", + "eth:VNT": "2.5.4", "eth:VS:888888": "2.4.4", + "eth:VSC": "2.5.4", "eth:W3G": "2.4.4", "eth:W3Q:333": "2.4.4", "eth:WAN": "2.4.2", "eth:WEB": "2.3.5", + "eth:WEMIX": "2.5.4", "eth:WGM": "2.4.4", "eth:WTT": "2.4.4", "eth:XDC": "2.3.7", "eth:XERO": "2.3.5", + "eth:XETA": "2.5.4", + "eth:XODEX": "2.5.4", "eth:XT": "2.5.2", "eth:XVM": "2.5.2", + "eth:XZO": "2.5.4", "eth:YCC": "2.5.2", "eth:YETI": "2.3.5", + "eth:ZENIQ": "2.5.4", "eth:ZENITH": "2.5.2", "eth:ZERO": "2.4.4", + "eth:ZETA": "2.5.4", + "eth:ZTH:427": "2.5.4", "eth:ZYX": "2.4.3", "eth:atp": "2.4.4", "eth:cTH": "2.4.2", "eth:cet": "2.4.2", + "eth:eBTC": "2.5.4", + "eth:hP2": "2.5.4", "eth:jfin": "2.5.2", "eth:lat": "2.4.4", "eth:mADA": "2.5.2", + "eth:mALGO": "2.5.4", + "eth:mc": "2.5.4", + "eth:nSAN": "2.5.4", "eth:pCKB": "2.5.2", "eth:peggle": "2.3.7", "eth:tATS": "2.0.11", "eth:tAVAX": "2.3.7", "eth:tBNB": "2.3.5", + "eth:tBVE": "2.5.4", "eth:tCELO:44787": "2.3.7", "eth:tCELO:62320": "2.3.7", "eth:tCFLR": "2.3.7", + "eth:tCRC": "2.5.4", "eth:tDBM": "2.3.5", - "eth:tGOR": "2.5.2", + "eth:tETH:11155111": "2.5.4", + "eth:tETH:3": "2.5.4", + "eth:tETH:4": "2.5.4", + "eth:tETH:42": "2.5.4", + "eth:tETH:5": "2.5.4", "eth:tKAL": "2.3.5", + "eth:tKEK": "2.5.4", "eth:tKLAY": "2.3.7", - "eth:tKOR": "2.3.7", "eth:tKOT": "2.3.5", - "eth:tKOV": "2.0.7", + "eth:tMAP": "2.5.4", "eth:tMATH": "2.3.7", "eth:tMATIC": "2.3.5", "eth:tMETC": "2.3.5", @@ -5001,16 +5449,15 @@ "eth:tNRG": "2.3.5", "eth:tPHT": "2.3.5", "eth:tRBTC": "2.0.7", - "eth:tRIN": "2.0.7", - "eth:tROP": "2.0.7", - "eth:tSEP": "2.4.4", "eth:tVT": "2.3.5", + "eth:tWIRE": "2.5.4", + "eth:taro": "2.5.4", "eth:thtt": "2.3.7", "eth:tmACA": "2.3.7", "eth:tsDIODE": "2.3.7", "eth:xDAI:100": "2.3.5", "eth:xDAI:300": "2.5.2", - "eth:\u03a6": "2.5.2", + "eth:xlon": "2.5.4", "eth:\u25c8": "2.5.2", "misc:ADA": "2.0.8", "misc:BNB": "2.1.5", @@ -5038,8 +5485,10 @@ "erc20:etc:PLAY": "(AUTO) duplicate key", "erc20:eth:A18:ba7d": "(AUTO) duplicate key", "erc20:eth:A18:bde8": "(AUTO) duplicate key", - "erc20:eth:ANT": "(AUTO) duplicate key", - "erc20:eth:ANT (old)": "(AUTO) duplicate key", + "erc20:eth:ADX:4470": "(AUTO) duplicate key", + "erc20:eth:ADX:ade0": "(AUTO) duplicate key", + "erc20:eth:ANT:960b": "(AUTO) duplicate key", + "erc20:eth:ANT:a117": "(AUTO) duplicate key", "erc20:eth:ATH": "(AUTO) duplicate key", "erc20:eth:ATH (AIgatha Token)": "(AUTO) duplicate key", "erc20:eth:ATS": "duplicate key with eth:ATS", @@ -5097,8 +5546,6 @@ "erc20:eth:CTT:e3fa": "(AUTO) duplicate key", "erc20:eth:DAO:0f51": "(AUTO) duplicate key", "erc20:eth:DAO:bb9b": "(AUTO) duplicate key", - "erc20:eth:DATA:0cf0": "(AUTO) duplicate key", - "erc20:eth:DATA:8f69": "(AUTO) duplicate key", "erc20:eth:DEPO": "(AUTO) duplicate key", "erc20:eth:DEPO (Depository Network)": "(AUTO) duplicate key", "erc20:eth:DGTX:1c83": "(AUTO) duplicate key", @@ -5174,6 +5621,8 @@ "erc20:eth:MDS:92b7": "(AUTO) duplicate key", "erc20:eth:MESH:01f2": "(AUTO) duplicate key", "erc20:eth:MESH:f030": "(AUTO) duplicate key", + "erc20:eth:MET:2ebd": "(AUTO) duplicate key", + "erc20:eth:MET:a3d5": "(AUTO) duplicate key", "erc20:eth:MIT": "(AUTO) duplicate key", "erc20:eth:MIT (Mychatcoin)": "(AUTO) duplicate key", "erc20:eth:MOC": "(AUTO) duplicate key", @@ -5295,31 +5744,72 @@ "eth:ATH:1620": "duplicate key", "eth:ATH:43110": "duplicate key", "eth:BCH": "duplicate of bitcoin:BCH", + "eth:BOBA:1294": "duplicate key", + "eth:BOBA:301": "duplicate key", + "eth:BOBA:43288": "duplicate key", + "eth:BOBA:56288": "duplicate key", + "eth:BOBA:97288": "duplicate key", + "eth:BRO:1039": "duplicate key", + "eth:BRO:108801": "duplicate key", "eth:BTX": "duplicate key", - "eth:CPAY:1337": "deprecated", + "eth:CAM:501": "duplicate key", + "eth:CCN:970": "duplicate key", + "eth:CCN:9700": "duplicate key", + "eth:CCN:971": "duplicate key", + "eth:DOGE": "duplicate key", "eth:ELA:22": "duplicate of ELA-ETH-sidechain", "eth:ELLA:64": "duplicate symbol", "eth:ELLA:7027": "duplicate symbol", "eth:EOS": "mainnet exists", "eth:ETH:10": "duplicate key", + "eth:ETH:10086": "duplicate key", + "eth:ETH:116": "duplicate key", "eth:ETH:1313161554": "duplicate key", "eth:ETH:1313161556": "duplicate key", "eth:ETH:1337802": "eth collision", + "eth:ETH:1337803": "duplicate key", "eth:ETH:288": "duplicate key", + "eth:ETH:324": "duplicate key", "eth:ETH:42161": "duplicate key", "eth:ETH:42170": "eth collision", + "eth:ETH:50001": "duplicate key", + "eth:ETH:534352": "duplicate key", "eth:ETH:5551": "eth collision", "eth:ETH:73927": "duplicate key", + "eth:ETH:8453": "duplicate key", "eth:ETH:980": "eth collision", + "eth:FIRE:5290": "duplicate key", + "eth:FRA:2152": "duplicate key", + "eth:FRA:2154": "duplicate key", "eth:GAR:91": "duplicate key", "eth:GAR:92": "duplicate key", "eth:GAR:93": "duplicate key", + "eth:GT:10024": "duplicate key", + "eth:GT:86": "duplicate key", "eth:GTH:192837465": "duplicate key", "eth:GTH:486217935": "exclude testnet", + "eth:HBAR:297": "duplicate key", + "eth:HBAR:298": "duplicate key", "eth:JEWEL:335": "exclude testnet", "eth:KTO:8285": "exclude testnet", + "eth:MCD:217": "duplicate key", + "eth:MCD:67390": "duplicate key", + "eth:METAL:381931": "duplicate key", + "eth:METAL:381932": "duplicate key", "eth:NEON:245022926": "duplicate key", + "eth:OAS:19011": "duplicate key", + "eth:OAS:2400": "duplicate key", + "eth:OAS:248": "duplicate key", + "eth:OAS:5555": "duplicate key", + "eth:OAS:7225878": "duplicate key", + "eth:OAS:876": "duplicate key", + "eth:ONE:1666900000": "duplicate key", "eth:PFT:808": "testnet", + "eth:PI:2099156": "duplicate key", + "eth:PI:8007736": "duplicate key", + "eth:POSI:900000": "duplicate key", + "eth:POSI:920000": "duplicate key", + "eth:POSI:920001": "duplicate key", "eth:Q": "causing problems in altcoin detection", "eth:QKC:110000": "exclude testnet", "eth:QKC:110001": "exclude testnet", @@ -5330,14 +5820,28 @@ "eth:QKC:110006": "exclude testnet", "eth:QKC:110007": "exclude testnet", "eth:QKC:110008": "exclude testnet", + "eth:QTZ:8881": "duplicate key", + "eth:QTZ:8883": "duplicate key", "eth:REI:55555": "duplicate key", "eth:ROC:1286": "deprecated", + "eth:ROSE:23294": "duplicate key", + "eth:ROSE:42262": "duplicate key", + "eth:SHM:8080": "duplicate key", + "eth:SHM:8081": "duplicate key", + "eth:SHM:8082": "duplicate key", "eth:SHYFT": "slip44 out of range", "eth:SOTER:218": "deprecated", "eth:SYS": "duplicate key", + "eth:T-FSN": "(AUTO) exclude testnet", + "eth:TARA:841": "duplicate key", + "eth:TARA:842": "(AUTO) exclude testnet", "eth:TAero": "(AUTO) exclude testnet", + "eth:TBITCI": "(AUTO) exclude testnet", + "eth:TBXN": "(AUTO) exclude testnet", "eth:TCRO": "(AUTO) exclude testnet", "eth:TELE:8001": "(AUTO) exclude testnet", + "eth:TEST": "(AUTO) exclude testnet", + "eth:TEXL": "(AUTO) exclude testnet", "eth:TFUEL:361": "incompatible", "eth:TFUEL:363": "(AUTO) exclude testnet", "eth:TFUEL:364": "(AUTO) exclude testnet", @@ -5353,56 +5857,128 @@ "eth:TKM:70103": "duplicate key", "eth:TLA": "(AUTO) exclude testnet", "eth:TLOS:41": "(AUTO) exclude testnet", + "eth:TOKI:8654": "duplicate key", + "eth:TOKI:8655": "(AUTO) exclude testnet", "eth:TOMO:89": "(AUTO) exclude testnet", "eth:TOP": "causing problems in altcoin detection", "eth:TOPC": "(AUTO) exclude testnet", + "eth:TPBX:404040": "duplicate key", + "eth:TPBX:4141": "(AUTO) exclude testnet", + "eth:TPI:10067275": "(AUTO) exclude testnet", + "eth:TPI:16658437": "(AUTO) exclude testnet", + "eth:TRES:6065": "(AUTO) exclude testnet", + "eth:TRES:6066": "duplicate key", "eth:TSCAS": "(AUTO) exclude testnet", + "eth:TSETH": "(AUTO) exclude testnet", + "eth:USD:2018": "duplicate key", + "eth:USD:2020": "duplicate key", "eth:VS:666666": "exclude testnet", "eth:W3Q:3334": "duplicate key", + "eth:ZTH:859": "duplicate key", + "eth:sFUEL:1482601649": "duplicate key", + "eth:sFUEL:1564830818": "duplicate key", + "eth:sFUEL:2046399126": "duplicate key", + "eth:sFUEL:503129905": "duplicate key", + "eth:t$BNI": "(AUTO) exclude testnet", "eth:tAAC": "(AUTO) exclude testnet", "eth:tACA": "(AUTO) exclude testnet", "eth:tAGOR": "testnet", "eth:tAIOZ": "(AUTO) exclude testnet", "eth:tAITD": "(AUTO) exclude testnet", "eth:tALOT": "(AUTO) exclude testnet", - "eth:tAMBROS": "(AUTO) exclude testnet", - "eth:tARETH": "(AUTO) exclude testnet", + "eth:tALYX": "(AUTO) exclude testnet", + "eth:tAMAX": "(AUTO) exclude testnet", + "eth:tAMB": "(AUTO) exclude testnet", + "eth:tASA": "(AUTO) exclude testnet", + "eth:tATN:65010000": "(AUTO) exclude testnet", + "eth:tATN:65100000": "(AUTO) exclude testnet", + "eth:tATOS": "(AUTO) exclude testnet", "eth:tAVIS": "(AUTO) exclude testnet", + "eth:tAcol": "(AUTO) exclude testnet", "eth:tBCHT": "(AUTO) exclude testnet", "eth:tBCS": "(AUTO) exclude testnet", "eth:tBELLY": "(AUTO) exclude testnet", - "eth:tBOBA": "(AUTO) exclude testnet", + "eth:tBFC": "(AUTO) exclude testnet", + "eth:tBIT": "(AUTO) exclude testnet", + "eth:tBLG": "(AUTO) exclude testnet", + "eth:tBOBA:1297": "(AUTO) exclude testnet", + "eth:tBOBA:4051": "(AUTO) exclude testnet", + "eth:tBOBA:4328": "(AUTO) exclude testnet", + "eth:tBOBA:9728": "(AUTO) exclude testnet", + "eth:tBOC": "(AUTO) exclude testnet", + "eth:tBOMB": "(AUTO) exclude testnet", + "eth:tBRNKC": "(AUTO) exclude testnet", + "eth:tBRO": "(AUTO) exclude testnet", "eth:tBTM": "(AUTO) exclude testnet", "eth:tBTT": "(AUTO) exclude testnet", + "eth:tC2FLR": "(AUTO) exclude testnet", + "eth:tCANTO": "(AUTO) exclude testnet", "eth:tCFX": "(AUTO) exclude testnet", + "eth:tCHZ": "(AUTO) exclude testnet", + "eth:tCICT": "(AUTO) exclude testnet", "eth:tCKB:71393": "(AUTO) exclude testnet", "eth:tCKB:868455272153094": "(AUTO) exclude testnet", + "eth:tCLD": "(AUTO) exclude testnet", + "eth:tCLO": "(AUTO) exclude testnet", "eth:tCLV": "(AUTO) exclude testnet", "eth:tCMP": "(AUTO) exclude testnet", + "eth:tCNT": "(AUTO) exclude testnet", + "eth:tCORE": "(AUTO) exclude testnet", "eth:tCTE": "testnet", "eth:tCUBET": "(AUTO) exclude testnet", "eth:tCWN": "(AUTO) exclude testnet", + "eth:tDB": "(AUTO) exclude testnet", + "eth:tDEL": "(AUTO) exclude testnet", + "eth:tDFI": "(AUTO) exclude testnet", + "eth:tDOGE": "(AUTO) exclude testnet", + "eth:tDOGS": "(AUTO) exclude testnet", "eth:tDTH": "(AUTO) exclude testnet", "eth:tDX": "(AUTO) exclude testnet", "eth:tDYNO": "(AUTO) exclude testnet", "eth:tECE": "(AUTO) exclude testnet", + "eth:tECG": "(AUTO) exclude testnet", "eth:tECO": "(AUTO) exclude testnet", "eth:tECS": "(AUTO) exclude testnet", "eth:tEDG": "(AUTO) exclude testnet", + "eth:tEDX": "(AUTO) exclude testnet", "eth:tELA:21": "(AUTO) exclude testnet", "eth:tELA:23": "(AUTO) exclude testnet", "eth:tEOS": "(AUTO) exclude testnet", + "eth:tETH:115": "(AUTO) exclude testnet", "eth:tETH:1313161555": "(AUTO) exclude testnet", + "eth:tETH:1337": "(AUTO) exclude testnet", + "eth:tETH:1402": "(AUTO) exclude testnet", + "eth:tETH:1422": "(AUTO) exclude testnet", "eth:tETH:28": "(AUTO) exclude testnet", "eth:tETH:280": "(AUTO) exclude testnet", + "eth:tETH:28528": "(AUTO) exclude testnet", + "eth:tETH:2888": "(AUTO) exclude testnet", "eth:tETH:420": "(AUTO) exclude testnet", + "eth:tETH:421611": "duplicate key", + "eth:tETH:534353": "(AUTO) exclude testnet", "eth:tETH:5553": "(AUTO) exclude testnet", + "eth:tETH:5777": "duplicate key", + "eth:tETH:69": "duplicate key", + "eth:tETH:84531": "(AUTO) exclude testnet", + "eth:tETH:956": "(AUTO) exclude testnet", + "eth:tETMP": "(AUTO) exclude testnet", "eth:tEUN": "(AUTO) exclude testnet", + "eth:tEVM": "(AUTO) exclude testnet", "eth:tEVMOS": "(AUTO) exclude testnet", "eth:tEZC": "(AUTO) exclude testnet", - "eth:tFETH": "(AUTO) exclude testnet", + "eth:tFIL:3141": "(AUTO) exclude testnet", + "eth:tFIL:31415": "(AUTO) exclude testnet", + "eth:tFIL:314159": "(AUTO) exclude testnet", + "eth:tFIL:3141592": "(AUTO) exclude testnet", + "eth:tFIL:31415926": "(AUTO) exclude testnet", + "eth:tFIRE:917": "(AUTO) exclude testnet", + "eth:tFIRE:9170": "(AUTO) exclude testnet", + "eth:tFITFI": "(AUTO) exclude testnet", + "eth:tFNCY": "(AUTO) exclude testnet", "eth:tFRA": "(AUTO) exclude testnet", "eth:tFTM": "(AUTO) exclude testnet", + "eth:tFTN": "(AUTO) exclude testnet", + "eth:tFtREN": "(AUTO) exclude testnet", "eth:tGAR:900": "(AUTO) exclude testnet", "eth:tGAR:901": "(AUTO) exclude testnet", "eth:tGAR:902": "(AUTO) exclude testnet", @@ -5410,20 +5986,34 @@ "eth:tGCD": "(AUTO) exclude testnet", "eth:tGO": "(AUTO) exclude testnet", "eth:tGT": "(AUTO) exclude testnet", + "eth:tGTFX": "(AUTO) exclude testnet", "eth:tGTH": "(AUTO) exclude testnet", + "eth:tGZN": "(AUTO) exclude testnet", "eth:tGooD": "(AUTO) exclude testnet", + "eth:tHBAR": "(AUTO) exclude testnet", + "eth:tHIK": "(AUTO) exclude testnet", "eth:tHOO": "(AUTO) exclude testnet", + "eth:tHPN": "(AUTO) exclude testnet", "eth:tHYM": "(AUTO) exclude testnet", + "eth:tIDE": "(AUTO) exclude testnet", + "eth:tIMV": "(AUTO) exclude testnet", "eth:tIOTX": "(AUTO) exclude testnet", + "eth:tISLMT": "(AUTO) exclude testnet", "eth:tIVAR": "(AUTO) exclude testnet", + "eth:tJINDA": "(AUTO) exclude testnet", "eth:tKAIBA": "(AUTO) exclude testnet", "eth:tKAR": "(AUTO) exclude testnet", "eth:tKCS": "(AUTO) exclude testnet", "eth:tKSX": "(AUTO) exclude testnet", + "eth:tKUB": "(AUTO) exclude testnet", "eth:tL1test": "(AUTO) exclude testnet", + "eth:tLAMB": "(AUTO) exclude testnet", + "eth:tLAVA": "(AUTO) exclude testnet", "eth:tMDGLT": "(AUTO) exclude testnet", + "eth:tMEER-T": "(AUTO) exclude testnet", "eth:tMESHT": "(AUTO) exclude testnet", - "eth:tMETIS": "(AUTO) exclude testnet", + "eth:tMETIS:588": "(AUTO) exclude testnet", + "eth:tMETIS:599": "(AUTO) exclude testnet", "eth:tMTR": "(AUTO) exclude testnet", "eth:tMTTest": "(AUTO) exclude testnet", "eth:tMYN": "(AUTO) exclude testnet", @@ -5431,6 +6021,10 @@ "eth:tNEON": "(AUTO) exclude testnet", "eth:tNMAC": "(AUTO) exclude testnet", "eth:tNTTH": "(AUTO) exclude testnet", + "eth:tNULS": "(AUTO) exclude testnet", + "eth:tNUM": "(AUTO) exclude testnet", + "eth:tNetZ": "(AUTO) exclude testnet", + "eth:tOAS": "(AUTO) exclude testnet", "eth:tOKT": "(AUTO) exclude testnet", "eth:tOLO": "(AUTO) exclude testnet", "eth:tOLT": "(AUTO) exclude testnet", @@ -5439,6 +6033,8 @@ "eth:tONE:1666700002": "(AUTO) exclude testnet", "eth:tONE:1666700003": "(AUTO) exclude testnet", "eth:tONG": "(AUTO) exclude testnet", + "eth:tONUS": "(AUTO) exclude testnet", + "eth:tOONE": "(AUTO) exclude testnet", "eth:tORING": "(AUTO) exclude testnet", "eth:tOY": "(AUTO) exclude testnet", "eth:tPALM": "(AUTO) exclude testnet", @@ -5447,38 +6043,66 @@ "eth:tPLS:941": "(AUTO) exclude testnet", "eth:tPLS:942": "(AUTO) exclude testnet", "eth:tPOLIS": "(AUTO) exclude testnet", + "eth:tPOSI": "(AUTO) exclude testnet", "eth:tPRB": "(AUTO) exclude testnet", "eth:tPRING": "(AUTO) exclude testnet", + "eth:tPRX": "(AUTO) exclude testnet", + "eth:tPTX": "(AUTO) exclude testnet", "eth:tQ": "causing problems in altcoin detection", "eth:tQDC": "(AUTO) exclude testnet", + "eth:tQET": "(AUTO) exclude testnet", + "eth:tQOM": "(AUTO) exclude testnet", "eth:tREI": "(AUTO) exclude testnet", - "eth:tRNA": "(AUTO) exclude testnet", "eth:tROSE": "(AUTO) exclude testnet", "eth:tRPG": "(AUTO) exclude testnet", "eth:tSFL": "(AUTO) exclude testnet", + "eth:tSHT": "(AUTO) exclude testnet", "eth:tSHYFTT": "(AUTO) exclude testnet", + "eth:tSINSO": "(AUTO) exclude testnet", "eth:tSNS": "(AUTO) exclude testnet", + "eth:tSRDX": "(AUTO) exclude testnet", + "eth:tSTAND": "(AUTO) exclude testnet", + "eth:tSTOS": "(AUTO) exclude testnet", + "eth:tSVRN": "(AUTO) exclude testnet", "eth:tSX": "(AUTO) exclude testnet", "eth:tSYS": "(AUTO) exclude testnet", + "eth:tU2U": "(AUTO) exclude testnet", "eth:tUBC": "(AUTO) exclude testnet", + "eth:tULX": "(AUTO) exclude testnet", + "eth:tUNQ": "(AUTO) exclude testnet", + "eth:tUSD": "(AUTO) exclude testnet", + "eth:tVET": "(AUTO) exclude testnet", "eth:tVIL": "(AUTO) exclude testnet", + "eth:tVNT": "(AUTO) exclude testnet", "eth:tW3G": "(AUTO) exclude testnet", "eth:tW3Q": "(AUTO) exclude testnet", "eth:tWAN": "(AUTO) exclude testnet", + "eth:tWEMIX": "(AUTO) exclude testnet", "eth:tWLK": "(AUTO) exclude testnet", "eth:tXPR": "(AUTO) exclude testnet", "eth:tXVM": "(AUTO) exclude testnet", "eth:tZCR": "(AUTO) exclude testnet", + "eth:tZEN": "(AUTO) exclude testnet", "eth:tZERO": "(AUTO) exclude testnet", + "eth:tZKST": "(AUTO) exclude testnet", + "eth:taZETA": "(AUTO) exclude testnet", "eth:tatp": "(AUTO) exclude testnet", "eth:tcett": "(AUTO) exclude testnet", "eth:tkiETH": "(AUTO) exclude testnet", "eth:tlat:2203181": "(AUTO) exclude testnet", "eth:tlat:2206132": "(AUTO) exclude testnet", "eth:tmTAda": "(AUTO) exclude testnet", + "eth:tmTAlgo": "(AUTO) exclude testnet", + "eth:tmc": "(AUTO) exclude testnet", "eth:tpCKB": "(AUTO) exclude testnet", + "eth:trAna": "(AUTO) exclude testnet", + "eth:tsFUEL": "(AUTO) exclude testnet", "eth:tusd": "(AUTO) exclude testnet", + "eth:txDAI:100100": "(AUTO) exclude testnet", + "eth:txDAI:10200": "(AUTO) exclude testnet", "eth:xDAI:200": "duplicate key", + "eth:\u03a6:144": "duplicate key", + "eth:\u03a6:4181": "duplicate key", "misc:LSK": "Incompatible mainnet hard-fork" } } diff --git a/common/protob/messages-management.proto b/common/protob/messages-management.proto index eb732fc6db..cc87c93387 100644 --- a/common/protob/messages-management.proto +++ b/common/protob/messages-management.proto @@ -27,6 +27,15 @@ enum SafetyCheckLevel { PromptTemporarily = 2; // like PromptAlways but reverts to Strict after reboot } + +/** + * Format of the homescreen image + */ +enum HomescreenFormat { + Toif144x144 = 1; + Jpeg240x240 = 2; +} + /** * Request: Reset device to default state and ask for device details * @start @@ -112,6 +121,8 @@ message Features { optional uint32 display_rotation = 39; // in degrees from North optional bool experimental_features = 40; // are experimental message types enabled? optional bool busy = 41; // is the device busy, showing "Do not disconnect"? + optional HomescreenFormat homescreen_format = 42; // format of the homescreen, 1 = TOIf 144x144, 2 = jpg 240x240 + optional bool hide_passphrase_from_host = 43; // should we hide the passphrase when it comes from host? } /** @@ -158,6 +169,7 @@ message ApplySettings { optional bool passphrase_always_on_device = 8; // do not prompt for passphrase, enforce device entry optional SafetyCheckLevel safety_checks = 9; // Safety check level, set to Prompt to limit path namespace enforcement optional bool experimental_features = 10; // enable experimental message types + optional bool hide_passphrase_from_host = 11; // do not show passphrase coming from host } /** diff --git a/common/tests/fixtures/ethereum/sign_tx_eip1559.json b/common/tests/fixtures/ethereum/sign_tx_eip1559.json index 239c6b07cd..d562539153 100644 --- a/common/tests/fixtures/ethereum/sign_tx_eip1559.json +++ b/common/tests/fixtures/ethereum/sign_tx_eip1559.json @@ -139,6 +139,25 @@ "sig_r": "3a5e8fa47bfdb758837643485b2951f6c54894996f124dce6282662289ebcf79", "sig_s": "5b5b9e0f9af273466cc1e991cb86fadb2abedeb4de150163a8dbf348422b5fe2" } + }, + { + "name": "long_fees", + "parameters": { + "data": "", + "path": "m/44'/60'/0'/0/0", + "to_address": "0x1d1c328764a41bda0492b66baa30c4a339ff85ef", + "chain_id": 1, + "nonce": "0x0", + "gas_limit": "0x141414141414141414", + "max_gas_fee": "0x14141414141414141414", + "max_priority_fee": "0x11111111111111111", + "value": "0xa" + }, + "result": { + "sig_v": 0, + "sig_r": "3f3bfa6762b33819f268a98744803e1876aa440a6fd2ebef90cfd606bb893429", + "sig_s": "241e1128a715a5386c3b6d0998f9f42c21ee080568fbf2c642a05916c30737e2" + } } ] } diff --git a/common/tests/fixtures/ethereum/signmessage.json b/common/tests/fixtures/ethereum/signmessage.json index 41f6df6794..6c55ef24a5 100644 --- a/common/tests/fixtures/ethereum/signmessage.json +++ b/common/tests/fixtures/ethereum/signmessage.json @@ -83,6 +83,16 @@ "address": "0x73d0385F4d8E00C5e6504C6030F47BF6212736A8", "sig": "fa9d60644436f27eb88956a50893e9a47f67c42fb1b57a44bde4c6e127ab777e0c23b234b6ec9327ffd0620daaa514243ebb5a3652a1bac2d720e0f5555b2e071c" } + }, + { + "parameters": { + "msg": "This is an example of a signed message at a different path.", + "path": "m/45'/60/2/1/1" + }, + "result": { + "address": "0x3beC5F707Ef56057354f4c062C53cd089E8Ea02C", + "sig": "27c75ccd53136ef1c470784d16440c798effbff4928fcee36a9080c2e929ce3f727aabcd4a0de88881ae02003bd16bc9d3f199d78266c41596db14fac134b44f1c" + } } ] } diff --git a/common/tools/cointool.py b/common/tools/cointool.py index 68960affc0..1de68cadf9 100755 --- a/common/tools/cointool.py +++ b/common/tools/cointool.py @@ -329,10 +329,11 @@ def check_dups(buckets: CoinBuckets, print_at_level: int = logging.WARNING) -> b and not coin_info.is_token(coin) ] # we do not count override-marked coins as duplicates here cleared = not any(coin.get("duplicate") for coin in bucket) + eth_testnet = symbol == "teth" # string generation dup_str = ", ".join(coin_str(coin) for coin in bucket) - if len(nontokens) > 1: + if len(nontokens) > 1 and not eth_testnet: # Two or more colliding nontokens. This is always fatal. # XXX consider allowing two nontokens as long as only one is supported? level = logging.ERROR diff --git a/core/.changelog.d/1922.changed b/core/.changelog.d/1922.changed new file mode 100644 index 0000000000..4ddc100bf1 --- /dev/null +++ b/core/.changelog.d/1922.changed @@ -0,0 +1 @@ +Switched to redesigned, Rust-based user interface. diff --git a/core/.changelog.d/2205.changed b/core/.changelog.d/2205.changed new file mode 100644 index 0000000000..799b01ec2a --- /dev/null +++ b/core/.changelog.d/2205.changed @@ -0,0 +1 @@ +Ignore channel ID in U2F. diff --git a/core/.changelog.d/2611.changed b/core/.changelog.d/2611.changed new file mode 100644 index 0000000000..af96c4c568 --- /dev/null +++ b/core/.changelog.d/2611.changed @@ -0,0 +1 @@ +Updated FAT FS library to R0.15 diff --git a/core/.changelog.d/2623.added b/core/.changelog.d/2623.added new file mode 100644 index 0000000000..3857a6bb9e --- /dev/null +++ b/core/.changelog.d/2623.added @@ -0,0 +1 @@ +Add model info to image and check when installing bootloader, prevent bootloader downgrade diff --git a/core/.changelog.d/2682.added b/core/.changelog.d/2682.added new file mode 100644 index 0000000000..265e0d1f31 --- /dev/null +++ b/core/.changelog.d/2682.added @@ -0,0 +1 @@ +Allow proposed Casa m/45' multisig paths for Bitcoin and Ethereum. diff --git a/core/.changelog.d/2746.changed b/core/.changelog.d/2746.changed new file mode 100644 index 0000000000..e336fbfb53 --- /dev/null +++ b/core/.changelog.d/2746.changed @@ -0,0 +1 @@ +Ethereum's EIP-712 signing no longer restricts the maximum field size to 1024 bytes. diff --git a/core/.changelog.d/2818.added b/core/.changelog.d/2818.added new file mode 100644 index 0000000000..82d3873401 --- /dev/null +++ b/core/.changelog.d/2818.added @@ -0,0 +1 @@ +Add address confirmation screen to EIP712 signing flow diff --git a/core/.changelog.d/2834.changed b/core/.changelog.d/2834.changed new file mode 100644 index 0000000000..8713fe9ccc --- /dev/null +++ b/core/.changelog.d/2834.changed @@ -0,0 +1 @@ +Force basic attestation in FIDO2 for google.com diff --git a/core/.changelog.d/2841.added b/core/.changelog.d/2841.added new file mode 100644 index 0000000000..6e2c35a395 --- /dev/null +++ b/core/.changelog.d/2841.added @@ -0,0 +1 @@ +Add the possibility of rebooting the device into bootloader mode diff --git a/core/.changelog.d/450.added b/core/.changelog.d/450.added new file mode 100644 index 0000000000..b44d2969b4 --- /dev/null +++ b/core/.changelog.d/450.added @@ -0,0 +1 @@ +CoSi collective signatures on Model T. diff --git a/core/.changelog.d/noissue.security b/core/.changelog.d/noissue.security new file mode 100644 index 0000000000..7318011cc7 --- /dev/null +++ b/core/.changelog.d/noissue.security @@ -0,0 +1 @@ +Match and validate script type of change-outputs in Bitcoin signing. diff --git a/core/.gitignore b/core/.gitignore index b48f5043bb..9a9ab8549f 100644 --- a/core/.gitignore +++ b/core/.gitignore @@ -8,3 +8,5 @@ tests/trezor_monero_tests* .coverage.* htmlcov/ mypy_report +/CMakeLists.txt +/cmake-build-debug/ diff --git a/core/Makefile b/core/Makefile index 9861c6c8e7..a94daa2e9b 100644 --- a/core/Makefile +++ b/core/Makefile @@ -25,7 +25,7 @@ BOOTLOADER_QA ?= 0 TREZOR_MODEL ?= T TREZOR_MEMPERF ?= 0 ADDRESS_SANITIZER ?= 0 -UI2 ?= 0 +CMAKELISTS ?= 0 # OpenOCD interface default. Alternative: ftdi/olimex-arm-usb-tiny-h OPENOCD_INTERFACE ?= stlink @@ -107,17 +107,28 @@ test_emu_fido2: ## run fido2 device tests test_emu_click: ## run click tests $(EMU_TEST) $(PYTEST) $(TESTPATH)/click_tests $(TESTOPTS) +test_emu_click_ui: ## run click tests with UI testing + $(EMU_TEST) $(PYTEST) $(TESTPATH)/click_tests --ui=test --ui-check-missing $(TESTOPTS) + test_emu_ui: ## run ui integration tests - $(EMU_TEST) $(PYTEST) $(TESTPATH)/device_tests --ui=test --ui-check-missing $(TESTOPTS) + $(EMU_TEST) $(PYTEST) $(TESTPATH)/device_tests $(TESTOPTS) \ + --ui=test --ui-check-missing --record-text-layout test_emu_ui_multicore: ## run ui integration tests using multiple cores - $(PYTEST) -n auto $(TESTPATH)/device_tests $(TESTOPTS) --ui=test --ui-check-missing --control-emulators --model=core --random-order-seed=$(shell echo $$RANDOM) + $(PYTEST) -n auto $(TESTPATH)/device_tests $(TESTOPTS) \ + --ui=test --ui-check-missing --record-text-layout \ + --control-emulators --model=core --random-order-seed=$(shell echo $$RANDOM) test_emu_ui_record: ## record and hash screens for ui integration tests - $(EMU_TEST) $(PYTEST) $(TESTPATH)/device_tests --ui=record --ui-check-missing $(TESTOPTS) + $(EMU_TEST) $(PYTEST) $(TESTPATH)/device_tests $(TESTOPTS) \ + --ui=record --ui-check-missing -test_emu_ui_record_multicore: ## record and hash screens for ui integration tests using multiple cores - $(PYTEST) -n auto $(TESTPATH)/device_tests $(TESTOPTS) --ui=record --ui-check-missing --control-emulators --model=core --random-order-seed=$(shell echo $$RANDOM) +test_emu_ui_record_multicore: ## quickly record all screens + make test_emu_ui_multicore || echo "All errors are recorded in fixtures.json" + make test_emu_accept_fixtures + +test_emu_accept_fixtures: # accept UI fixtures from the last run of UI tests + ../tests/update_fixtures.py pylint: ## run pylint on application sources and tests pylint -E $(shell find src tests -name *.py) @@ -149,33 +160,33 @@ build: build_boardloader build_bootloader build_firmware build_prodtest build_un build_embed: build_boardloader build_bootloader build_firmware # build boardloader, bootloader, firmware build_boardloader: ## build boardloader - $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" $(BOARDLOADER_BUILD_DIR)/boardloader.bin + $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" $(BOARDLOADER_BUILD_DIR)/boardloader.bin build_bootloader: ## build bootloader - $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" $(BOOTLOADER_BUILD_DIR)/bootloader.bin + $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" $(BOOTLOADER_BUILD_DIR)/bootloader.bin build_bootloader_ci: ## build CI device testing bootloader - $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" $(BOOTLOADER_CI_BUILD_DIR)/bootloader.bin + $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" $(BOOTLOADER_CI_BUILD_DIR)/bootloader.bin build_prodtest: ## build production test firmware - $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" $(PRODTEST_BUILD_DIR)/prodtest.bin + $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" $(PRODTEST_BUILD_DIR)/prodtest.bin build_reflash: ## build reflash firmware + reflash image - $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" $(REFLASH_BUILD_DIR)/reflash.bin + $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" $(REFLASH_BUILD_DIR)/reflash.bin dd if=build/boardloader/boardloader.bin of=$(REFLASH_BUILD_DIR)/sdimage.bin bs=1 seek=0 dd if=build/bootloader/bootloader.bin of=$(REFLASH_BUILD_DIR)/sdimage.bin bs=1 seek=49152 build_firmware: templates build_cross ## build firmware with frozen modules - $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" PYOPT="$(PYOPT)" BITCOIN_ONLY="$(BITCOIN_ONLY)" BOOTLOADER_QA="$(BOOTLOADER_QA)" $(FIRMWARE_BUILD_DIR)/firmware.bin + $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" PYOPT="$(PYOPT)" BITCOIN_ONLY="$(BITCOIN_ONLY)" BOOTLOADER_QA="$(BOOTLOADER_QA)" $(FIRMWARE_BUILD_DIR)/firmware.bin build_unix: templates ## build unix port - $(SCONS) CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) TREZOR_MODEL="$(TREZOR_MODEL)" PYOPT="0" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)" + $(SCONS) CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" PYOPT="0" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)" build_unix_frozen: templates build_cross ## build unix port with frozen modules - $(SCONS) CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) TREZOR_MODEL="$(TREZOR_MODEL)" PYOPT="$(PYOPT)" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)" TREZOR_MEMPERF="$(TREZOR_MEMPERF)" TREZOR_EMULATOR_FROZEN=1 + $(SCONS) CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" PYOPT="$(PYOPT)" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)" TREZOR_MEMPERF="$(TREZOR_MEMPERF)" TREZOR_EMULATOR_FROZEN=1 build_unix_debug: templates ## build unix port - $(SCONS) --max-drift=1 CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) TREZOR_MODEL="$(TREZOR_MODEL)" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN=1 TREZOR_EMULATOR_DEBUGGABLE=1 + $(SCONS) --max-drift=1 CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN=1 TREZOR_EMULATOR_DEBUGGABLE=1 build_cross: ## build mpy-cross port $(MAKE) -C vendor/micropython/mpy-cross $(CROSS_PORT_OPTS) @@ -309,5 +320,8 @@ upload: ## upload firmware using trezorctl upload_prodtest: ## upload prodtest using trezorctl trezorctl firmware_update -f $(PRODTEST_BUILD_DIR)/prodtest.bin -coverage: # generate coverage report +coverage: ## generate coverage report ./tools/coverage-report + +unused: ## find unused micropython code + vulture src src/_vulture_ignore.txt --exclude "messages.py,*/enums/*" diff --git a/core/SConscript.boardloader b/core/SConscript.boardloader index de31c8c711..d94b5f6c9c 100644 --- a/core/SConscript.boardloader +++ b/core/SConscript.boardloader @@ -4,6 +4,7 @@ import os import tools TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T') +CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0)) if TREZOR_MODEL in ('1', ): # skip boardloader build @@ -104,13 +105,16 @@ env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s' % (ARGUMENTS.get(' tools.configure_board(TREZOR_MODEL, env, CPPDEFINES_MOD, SOURCE_TREZORHAL) env.Replace( + CP='cp', AS='arm-none-eabi-as', AR='arm-none-eabi-ar', CC='arm-none-eabi-gcc', LINK='arm-none-eabi-gcc', SIZE='arm-none-eabi-size', STRIP='arm-none-eabi-strip', - OBJCOPY='arm-none-eabi-objcopy', ) + OBJCOPY='arm-none-eabi-objcopy', + PYTHON='python', + MAKECMAKELISTS='$PYTHON tools/make_cmakelists.py',) env.Replace( TREZOR_MODEL=TREZOR_MODEL, ) @@ -133,7 +137,7 @@ env.Replace( '-fstack-protector-all ' + CPU_CCFLAGS + CCFLAGS_MOD, CCFLAGS_QSTR='-DNO_QSTR -DN_X64 -DN_X86 -DN_THUMB', - LINKFLAGS='-T embed/boardloader/memory.ld -Wl,--gc-sections -Wl,-Map=build/boardloader/boardloader.map -Wl,--warn-common', + LINKFLAGS='-T embed/boardloader/memory.ld -Wl,--gc-sections -Wl,-Map=build/boardloader/boardloader.map -Wl,--warn-common -Wl,--print-memory-usage', CPPPATH=[ 'embed/boardloader', 'embed/trezorhal', @@ -153,6 +157,16 @@ env.Replace( ASFLAGS=CPU_ASFLAGS, ASPPFLAGS='$CFLAGS $CCFLAGS', ) +env.Replace( + ALLSOURCES=SOURCE_MOD + SOURCE_BOARDLOADER + SOURCE_STMHAL + SOURCE_TREZORHAL, + ALLDEFS=tools.get_defs_for_cmake(env['CPPDEFINES'])) + +cmake_gen = env.Command( + target='CMakeLists.txt', + source='', + action='$MAKECMAKELISTS --sources $ALLSOURCES --dirs $CPPPATH --defs $ALLDEFS', +) + # # Program objects # @@ -170,8 +184,20 @@ program_elf = env.Command( '$LINK -o $TARGET $CCFLAGS $CFLAGS $LINKFLAGS $SOURCES -lc_nano -lgcc', ) +BINARY_NAME = f"build/boardloader/boardloader-{tools.get_model_identifier(TREZOR_MODEL)}" +BINARY_NAME += "-" + tools.get_version('embed/boardloader/version.h') +BINARY_NAME += "-" + tools.get_git_revision_short_hash() +BINARY_NAME += "-dirty" if tools.get_git_modified() else "" +BINARY_NAME += ".bin" + +if CMAKELISTS != 0: + env.Depends(program_elf, cmake_gen) + program_bin = env.Command( target='boardloader.bin', source=program_elf, - action='$OBJCOPY -O binary $SOURCE $TARGET', + action=[ + '$OBJCOPY -O binary $SOURCE $TARGET', + '$CP $TARGET ' + BINARY_NAME, + ], ) diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader index c9829affd7..0c396c5904 100644 --- a/core/SConscript.bootloader +++ b/core/SConscript.bootloader @@ -4,6 +4,7 @@ import os import tools TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T') +CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0)) DMA2D = False if TREZOR_MODEL in ('1', ): @@ -157,13 +158,16 @@ env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s' % (ARGUMENTS.get(' tools.configure_board(TREZOR_MODEL, env, CPPDEFINES_MOD, SOURCE_TREZORHAL) env.Replace( + CP='cp', AS='arm-none-eabi-as', AR='arm-none-eabi-ar', CC='arm-none-eabi-gcc', LINK='arm-none-eabi-gcc', SIZE='arm-none-eabi-size', STRIP='arm-none-eabi-strip', - OBJCOPY='arm-none-eabi-objcopy', ) + OBJCOPY='arm-none-eabi-objcopy', + PYTHON='python', + MAKECMAKELISTS='$PYTHON tools/make_cmakelists.py', ) env.Replace( TREZOR_MODEL=TREZOR_MODEL, ) @@ -192,7 +196,7 @@ env.Replace( '-fstack-protector-all ' + CPU_CCFLAGS + CCFLAGS_MOD, CCFLAGS_QSTR='-DNO_QSTR -DN_X64 -DN_X86 -DN_THUMB', - LINKFLAGS='-T embed/bootloader/memory.ld -Wl,--gc-sections -Wl,-Map=build/bootloader/bootloader.map -Wl,--warn-common', + LINKFLAGS='-T embed/bootloader/memory.ld -Wl,--gc-sections -Wl,-Map=build/bootloader/bootloader.map -Wl,--warn-common -Wl,--print-memory-usage', CPPPATH=[ 'embed/rust', 'embed/bootloader', @@ -222,6 +226,17 @@ env.Replace( HEADERTOOL='tools/headertool.py', ) +env.Replace( + ALLSOURCES=SOURCE_MOD + SOURCE_BOOTLOADER + SOURCE_NANOPB + SOURCE_STMHAL + SOURCE_TREZORHAL, + ALLDEFS=tools.get_defs_for_cmake(env['CPPDEFINES'])) + +cmake_gen = env.Command( + target='CMakeLists.txt', + source='', + action='$MAKECMAKELISTS --sources $ALLSOURCES --dirs $CPPPATH --defs $ALLDEFS', +) + + # # Program objects # @@ -240,10 +255,20 @@ program_elf = env.Command( '$LINK -o $TARGET $CCFLAGS $CFLAGS $LINKFLAGS $SOURCES -lc_nano -lgcc', ) +BINARY_NAME = f"build/bootloader/bootloader-{tools.get_model_identifier(TREZOR_MODEL)}" +BINARY_NAME += "-" + tools.get_version('embed/bootloader/version.h') +BINARY_NAME += "-" + tools.get_git_revision_short_hash() +BINARY_NAME += "-dirty" if tools.get_git_modified() else "" +BINARY_NAME += ".bin" + +if CMAKELISTS != 0: + env.Depends(program_elf, cmake_gen) + program_bin = env.Command( target='bootloader.bin', source=program_elf, action=[ '$OBJCOPY -O binary -j .header -j .flash -j .data $SOURCE $TARGET', '$HEADERTOOL $TARGET ' + ('-D' if ARGUMENTS.get('PRODUCTION', '0') == '0' else ''), + '$CP $TARGET ' + BINARY_NAME, ], ) diff --git a/core/SConscript.bootloader_ci b/core/SConscript.bootloader_ci index f7b99791d9..eecc7d0416 100644 --- a/core/SConscript.bootloader_ci +++ b/core/SConscript.bootloader_ci @@ -4,6 +4,7 @@ import os import tools TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T') +CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0)) if TREZOR_MODEL in ('1', ): # skip bootloader_ci build @@ -140,13 +141,16 @@ env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s' % (ARGUMENTS.get(' tools.configure_board(TREZOR_MODEL, env, CPPDEFINES_MOD, SOURCE_TREZORHAL) env.Replace( + CP='cp', AS='arm-none-eabi-as', AR='arm-none-eabi-ar', CC='arm-none-eabi-gcc', LINK='arm-none-eabi-gcc', SIZE='arm-none-eabi-size', STRIP='arm-none-eabi-strip', - OBJCOPY='arm-none-eabi-objcopy', ) + OBJCOPY='arm-none-eabi-objcopy', + PYTHON='python', + MAKECMAKELISTS='$PYTHON tools/make_cmakelists.py',) env.Replace( TREZOR_MODEL=TREZOR_MODEL, ) @@ -191,12 +195,24 @@ env.Replace( 'PB_VALIDATE_UTF8', ] + CPPDEFINES_MOD, ASFLAGS=CPU_ASFLAGS, - ASPPFLAGS='$CFLAGS $CCFLAGS', ) + ASPPFLAGS='$CFLAGS $CCFLAGS', + ALLSOURCES=SOURCE_MOD + SOURCE_BOOTLOADER + SOURCE_STMHAL + SOURCE_TREZORHAL+ SOURCE_NANOPB, ) env.Replace( HEADERTOOL='tools/headertool.py', ) +env.Replace( + ALLSOURCES=SOURCE_MOD + SOURCE_BOOTLOADER + SOURCE_NANOPB + SOURCE_STMHAL + SOURCE_TREZORHAL, + ALLDEFS=tools.get_defs_for_cmake(env['CPPDEFINES'])) + +cmake_gen = env.Command( + target='CMakeLists.txt', + source='', + action='$MAKECMAKELISTS --sources $ALLSOURCES --dirs $CPPPATH --defs $ALLDEFS', +) + + # # Program objects # @@ -215,10 +231,20 @@ program_elf = env.Command( '$LINK -o $TARGET $CCFLAGS $CFLAGS $LINKFLAGS $SOURCES -lc_nano -lgcc', ) +BINARY_NAME = f"build/bootloader_ci/bootloader_ci-{tools.get_model_identifier(TREZOR_MODEL)}" +BINARY_NAME += "-" + tools.get_version('embed/bootloader_ci/version.h') +BINARY_NAME += "-" + tools.get_git_revision_short_hash() +BINARY_NAME += "-dirty" if tools.get_git_modified() else "" +BINARY_NAME += ".bin" + +if CMAKELISTS != 0: + env.Depends(program_elf, cmake_gen) + program_bin = env.Command( target='bootloader.bin', source=program_elf, action=[ '$OBJCOPY -O binary -j .header -j .flash -j .data $SOURCE $TARGET', '$HEADERTOOL $TARGET ' + ('-D' if ARGUMENTS.get('PRODUCTION', '0') == '0' else ''), + '$CP $TARGET ' + BINARY_NAME, ], ) diff --git a/core/SConscript.firmware b/core/SConscript.firmware index 023a53965e..8bcb5c2c09 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -10,6 +10,7 @@ BOOTLOADER_QA = ARGUMENTS.get('BOOTLOADER_QA', '0') == '1' EVERYTHING = BITCOIN_ONLY != '1' TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T') DMA2D = TREZOR_MODEL in ('T', ) +CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0)) if PRODUCTION != '1' and BOOTLOADER_QA: raise ValueError('Firmware variant for bootloader upgrade testing must be done with PRODUCTION=1') @@ -188,6 +189,7 @@ SOURCE_MOD += [ 'vendor/micropython/lib/uzlib/crc32.c', 'vendor/micropython/lib/uzlib/tinflate.c', ] + CPPDEFINES_MOD += [ 'TREZOR_UI2', 'USE_RUST_LOADER' @@ -445,6 +447,7 @@ env.Tool('micropython') env.Replace( CAT='cat', DD='dd', + CP='cp', SED='sed', AS='arm-none-eabi-as', AR='arm-none-eabi-ar', @@ -483,7 +486,7 @@ env.Replace( '-fstack-protector-all ' + CPU_CCFLAGS + CCFLAGS_MOD, CCFLAGS_QSTR='-DNO_QSTR -DN_X64 -DN_X86 -DN_THUMB', - LINKFLAGS='-T embed/firmware/memory_${TREZOR_MODEL}%s.ld -Wl,--gc-sections -Wl,-Map=build/firmware/firmware.map -Wl,--warn-common' % LD_VARIANT, + LINKFLAGS='-T embed/firmware/memory_${TREZOR_MODEL}%s.ld -Wl,--gc-sections -Wl,--print-memory-usage -Wl,-Map=build/firmware/firmware.map -Wl,--warn-common' % LD_VARIANT, CPPPATH=[ '.', 'embed/rust', @@ -511,6 +514,7 @@ env.Replace( MAKEQSTRDATA='$PYTHON vendor/micropython/py/makeqstrdata.py', MAKEVERSIONHDR='$PYTHON vendor/micropython/py/makeversionhdr.py', MAKEMODULEDEFS='$PYTHON vendor/micropython/py/makemoduledefs.py', + MAKECMAKELISTS='$PYTHON tools/make_cmakelists.py', MPY_TOOL='$PYTHON vendor/micropython/tools/mpy-tool.py', MPY_CROSS='vendor/micropython/mpy-cross/mpy-cross -O' + PYOPT, PB2PY='$PYTHON ../common/protob/pb2py', @@ -575,18 +579,19 @@ if FROZEN: SOURCE_PY = Glob(SOURCE_PY_DIR + '*.py') SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/crypto/*.py')) - SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/res/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/common/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/__init__.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/common.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/homescreen.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/reset.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/recovery.py')) if EVERYTHING: SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/fido.py')) if TREZOR_MODEL in ('T',): SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/__init__.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/homescreen.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/reset.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/recovery.py')) if EVERYTHING: @@ -717,7 +722,9 @@ def cargo_build(): else: profile = '' - features = ['micropython', 'protobuf', f'model_t{TREZOR_MODEL.lower()}'] + # T1 does not have its own Rust feature, it shares it with TR + model_feature = 'model_tr' if TREZOR_MODEL == '1' else f'model_t{TREZOR_MODEL.lower()}' + features = ['micropython', 'protobuf', model_feature] if BITCOIN_ONLY == '1': features.append('bitcoin_only') features.append('ui') @@ -752,10 +759,12 @@ env.Append(LINKFLAGS=f' -l{RUST_LIB}') # Program objects # +source_files = SOURCE_MOD + SOURCE_FIRMWARE + SOURCE_MICROPYTHON + SOURCE_MICROPYTHON_SPEED + SOURCE_STMHAL + SOURCE_TREZORHAL obj_program = [] obj_program.extend(env.Object(source=SOURCE_MOD)) if FEATURE_FLAGS["SECP256K1_ZKP"]: obj_program.extend(env.Object(source=SOURCE_MOD_SECP256K1_ZKP, CCFLAGS='$CCFLAGS -Wno-unused-function')) + source_files.extend(SOURCE_MOD_SECP256K1_ZKP) obj_program.extend(env.Object(source=SOURCE_FIRMWARE)) obj_program.extend(env.Object(source=SOURCE_MICROPYTHON)) obj_program.extend(env.Object(source=SOURCE_MICROPYTHON_SPEED, COPT='-O3')) @@ -764,6 +773,18 @@ obj_program.extend(env.Object(source=SOURCE_TREZORHAL)) if FROZEN: obj_program.extend(env.Object(source=source_mpyc)) +env.Replace( + ALLSOURCES=source_files, + ALLDEFS=tools.get_defs_for_cmake(env['CPPDEFINES'])) + + +cmake_gen = env.Command( + target='CMakeLists.txt', + source='', + action='$MAKECMAKELISTS --sources $ALLSOURCES --dirs $CPPPATH --defs $ALLDEFS', +) + + VENDORHEADER = 'embed/vendorheader/vendorheader_' + ('unsafe_signed_prod.bin' if ARGUMENTS.get('PRODUCTION', '0') == '0' else 'satoshilabs_signed_prod.bin') obj_program.extend( @@ -775,7 +796,7 @@ obj_program.extend( ' $SOURCE $TARGET', )) -BOOTLOADER_SUFFIX = TREZOR_MODEL + ('_QA' if BOOTLOADER_QA else '') +BOOTLOADER_SUFFIX = tools.get_model_identifier(TREZOR_MODEL) + ('_QA' if BOOTLOADER_QA else '') obj_program.extend( env.Command( @@ -797,8 +818,19 @@ program_elf = env.Command( '$LINK -o $TARGET $CCFLAGS $CFLAGS $SOURCES $LINKFLAGS -lc_nano -lm -lgcc', ) +if CMAKELISTS != 0: + env.Depends(program_elf, cmake_gen) env.Depends(program_elf, rust) +BINARY_NAME = f"build/firmware/firmware-{tools.get_model_identifier(TREZOR_MODEL)}" +if not EVERYTHING: + BINARY_NAME += "-btconly" +BINARY_NAME += "-" + tools.get_version('embed/firmware/version.h') +BINARY_NAME += "-" + tools.get_git_revision_short_hash() +BINARY_NAME += "-dirty" if tools.get_git_modified() else "" +BINARY_NAME += ".bin" + + if TREZOR_MODEL in ('T', 'R'): action_bin=[ '$OBJCOPY -O binary -j .vendorheader -j .header -j .flash -j .data --pad-to 0x08100000 $SOURCE ${TARGET}.p1', @@ -806,11 +838,13 @@ if TREZOR_MODEL in ('T', 'R'): '$CAT ${TARGET}.p1 ${TARGET}.p2 > $TARGET', '$HEADERTOOL -h $TARGET ' + ('-D' if ARGUMENTS.get('PRODUCTION', '0') == '0' else ''), '$DD if=$TARGET of=${TARGET}.p1 skip=0 bs=128k count=6', + '$CP $TARGET ' + BINARY_NAME, ] elif TREZOR_MODEL in ('1',): action_bin=[ '$OBJCOPY -O binary -j .header -j .flash -j .data $SOURCE $TARGET', '../legacy/bootloader/firmware_sign.py -f $TARGET', + '$CP $TARGET ' + BINARY_NAME, ] else: raise ValueError('Unknown Trezor model') diff --git a/core/SConscript.prodtest b/core/SConscript.prodtest index ba19d4dd19..829c329713 100644 --- a/core/SConscript.prodtest +++ b/core/SConscript.prodtest @@ -4,6 +4,7 @@ import os import tools TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T') +CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0)) CCFLAGS_MOD = '' CPPPATH_MOD = [] @@ -109,13 +110,16 @@ env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s' % (ARGUMENTS.get(' tools.configure_board(TREZOR_MODEL, env, CPPDEFINES_MOD, SOURCE_TREZORHAL) env.Replace( + CP='cp', AS='arm-none-eabi-as', AR='arm-none-eabi-ar', CC='arm-none-eabi-gcc', LINK='arm-none-eabi-gcc', SIZE='arm-none-eabi-size', STRIP='arm-none-eabi-strip', - OBJCOPY='arm-none-eabi-objcopy', ) + OBJCOPY='arm-none-eabi-objcopy', + PYTHON='python', + MAKECMAKELISTS='$PYTHON tools/make_cmakelists.py',) env.Replace( TREZOR_MODEL=TREZOR_MODEL, ) @@ -164,6 +168,18 @@ env.Replace( HEADERTOOL='tools/headertool.py', ) + +env.Replace( + ALLSOURCES=SOURCE_MOD + SOURCE_PRODTEST + SOURCE_STMHAL + SOURCE_TREZORHAL, + ALLDEFS=tools.get_defs_for_cmake(env['CPPDEFINES'])) + +cmake_gen = env.Command( + target='CMakeLists.txt', + source='', + action='$MAKECMAKELISTS --sources $ALLSOURCES --dirs $CPPPATH --defs $ALLDEFS', +) + + # # Program objects # @@ -191,10 +207,20 @@ program_elf = env.Command( '$LINK -o $TARGET $CCFLAGS $CFLAGS $LINKFLAGS $SOURCES -lc_nano -lgcc', ) +BINARY_NAME = f"build/prodtest/prodtest-{tools.get_model_identifier(TREZOR_MODEL)}" +BINARY_NAME += "-" + tools.get_version('embed/prodtest/version.h') +BINARY_NAME += "-" + tools.get_git_revision_short_hash() +BINARY_NAME += "-dirty" if tools.get_git_modified() else "" +BINARY_NAME += ".bin" + +if CMAKELISTS != 0: + env.Depends(program_elf, cmake_gen) + program_bin = env.Command( target='prodtest.bin', source=program_elf, action=[ '$OBJCOPY -O binary -j .vendorheader -j .header -j .flash -j .data $SOURCE $TARGET', '$HEADERTOOL $TARGET ' + ('-D' if ARGUMENTS.get('PRODUCTION', '0') == '0' else ''), + '$CP $TARGET ' + BINARY_NAME, ], ) diff --git a/core/SConscript.reflash b/core/SConscript.reflash index e10b523115..be8fafe10e 100644 --- a/core/SConscript.reflash +++ b/core/SConscript.reflash @@ -4,6 +4,7 @@ import os import tools TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T') +CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0)) CCFLAGS_MOD = '' CPPPATH_MOD = [] @@ -37,7 +38,7 @@ SOURCE_MOD += [ 'embed/extmod/modtrezorui/display.c', 'embed/extmod/modtrezorui/colors.c', 'embed/extmod/modtrezorui/fonts/fonts.c', - 'embed/extmod/modtrezorui/font_bitmap.c', + 'embed/extmod/modtrezorui/fonts/font_bitmap.c', 'vendor/micropython/lib/uzlib/adler32.c', 'vendor/micropython/lib/uzlib/crc32.c', 'vendor/micropython/lib/uzlib/tinflate.c', @@ -102,13 +103,16 @@ env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s' % (ARGUMENTS.get(' tools.configure_board(TREZOR_MODEL, env, CPPDEFINES_MOD, SOURCE_TREZORHAL) env.Replace( + CP='cp', AS='arm-none-eabi-as', AR='arm-none-eabi-ar', CC='arm-none-eabi-gcc', LINK='arm-none-eabi-gcc', SIZE='arm-none-eabi-size', STRIP='arm-none-eabi-strip', - OBJCOPY='arm-none-eabi-objcopy', ) + OBJCOPY='arm-none-eabi-objcopy', + PYTHON='python', + MAKECMAKELISTS='$PYTHON tools/make_cmakelists.py',) env.Replace( TREZOR_MODEL=TREZOR_MODEL, ) @@ -157,6 +161,16 @@ env.Replace( HEADERTOOL='tools/headertool.py', ) +env.Replace( + ALLSOURCES=SOURCE_MOD + SOURCE_REFLASH + SOURCE_STMHAL + SOURCE_TREZORHAL, + ALLDEFS=tools.get_defs_for_cmake(env['CPPDEFINES'])) + +cmake_gen = env.Command( + target='CMakeLists.txt', + source='', + action='$MAKECMAKELISTS --sources $ALLSOURCES --dirs $CPPPATH --defs $ALLDEFS', +) + # # Program objects # @@ -184,10 +198,20 @@ program_elf = env.Command( '$LINK -o $TARGET $CCFLAGS $CFLAGS $LINKFLAGS $SOURCES -lc_nano -lgcc', ) +BINARY_NAME = f"build/reflash/reflash-{tools.get_model_identifier(TREZOR_MODEL)}" +BINARY_NAME += "-" + tools.get_version('embed/reflash/version.h') +BINARY_NAME += "-" + tools.get_git_revision_short_hash() +BINARY_NAME += "-dirty" if tools.get_git_modified() else "" +BINARY_NAME += ".bin" + +if CMAKELISTS != 0: + env.Depends(program_elf, cmake_gen) + program_bin = env.Command( target='reflash.bin', source=program_elf, action=[ '$OBJCOPY -O binary -j .vendorheader -j .header -j .flash -j .data $SOURCE $TARGET', '$HEADERTOOL $TARGET ' + ('-D' if ARGUMENTS.get('PRODUCTION', '0') == '0' else ''), + '$CP $TARGET ' + BINARY_NAME, ], ) diff --git a/core/SConscript.unix b/core/SConscript.unix index b8ae28f737..20fd41146f 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -8,6 +8,7 @@ BITCOIN_ONLY = ARGUMENTS.get('BITCOIN_ONLY', '0') EVERYTHING = BITCOIN_ONLY != '1' TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T') DMA2D = TREZOR_MODEL in ('T', ) +CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0)) FEATURE_FLAGS = { "SECP256K1_ZKP": True, # required for trezor.crypto.curve.bip340 (BIP340/Taproot) @@ -460,6 +461,7 @@ env.Replace( MAKEQSTRDATA='$PYTHON vendor/micropython/py/makeqstrdata.py', MAKEVERSIONHDR='$PYTHON vendor/micropython/py/makeversionhdr.py', MAKEMODULEDEFS='$PYTHON vendor/micropython/py/makemoduledefs.py', + MAKECMAKELISTS='$PYTHON tools/make_cmakelists.py', MPY_TOOL='$PYTHON vendor/micropython/tools/mpy-tool.py', MPY_CROSS='vendor/micropython/mpy-cross/mpy-cross -O' + PYOPT, PB2PY='$PYTHON ../common/protob/pb2py', @@ -524,18 +526,19 @@ if FROZEN: SOURCE_PY = Glob(SOURCE_PY_DIR + '*.py') SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/crypto/*.py')) - SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/res/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/common/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/__init__.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/common.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/homescreen.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/reset.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/recovery.py')) if EVERYTHING: SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/fido.py')) if TREZOR_MODEL in ('T',): SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/__init__.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/homescreen.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/reset.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/recovery.py')) if EVERYTHING: @@ -666,7 +669,9 @@ RUST_LIB = 'trezor_lib' RUST_LIBPATH = f'{RUST_LIBDIR}/lib{RUST_LIB}.a' def cargo_build(): - features = ['micropython', 'protobuf', f'model_t{TREZOR_MODEL.lower()}'] + # T1 does not have its own Rust feature, it shares it with TR + model_feature = 'model_tr' if TREZOR_MODEL == '1' else f'model_t{TREZOR_MODEL.lower()}' + features = ['micropython', 'protobuf', model_feature] if BITCOIN_ONLY == '1': features.append('bitcoin_only') features.append('ui') @@ -694,14 +699,28 @@ env.Append(LINKFLAGS=f'-l{RUST_LIB}') # obj_program = [] +source_files = SOURCE_MOD + SOURCE_MICROPYTHON + SOURCE_UNIX obj_program.extend(env.Object(source=SOURCE_MOD)) if FEATURE_FLAGS["SECP256K1_ZKP"]: obj_program.extend(env.Object(source=SOURCE_MOD_SECP256K1_ZKP, CCFLAGS='$CCFLAGS -Wno-unused-function')) + source_files.extend(SOURCE_MOD_SECP256K1_ZKP) obj_program.extend(env.Object(source=SOURCE_MICROPYTHON)) obj_program.extend(env.Object(source=SOURCE_UNIX)) if FROZEN: obj_program.extend(env.Object(source=source_mpyc)) +env.Replace( + ALLSOURCES=source_files, + ALLDEFS=tools.get_defs_for_cmake(env['CPPDEFINES'])) + + +cmake_gen = env.Command( + target='CMakeLists.txt', + source='', + action='$MAKECMAKELISTS --sources $ALLSOURCES --dirs $CPPPATH --defs $ALLDEFS', +) + + env.Depends(obj_program, qstr_generated) program = env.Command( @@ -709,4 +728,6 @@ program = env.Command( source=obj_program, action='$CC -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $LINKFLAGS', ) +if CMAKELISTS != 0: + env.Depends(program, cmake_gen) env.Depends(program, rust) diff --git a/core/assets/click-icon.png b/core/assets/click-icon.png deleted file mode 100644 index ddf37eee9c..0000000000 Binary files a/core/assets/click-icon.png and /dev/null differ diff --git a/core/assets/cog.png b/core/assets/cog.png deleted file mode 100644 index 8c59f68605..0000000000 Binary files a/core/assets/cog.png and /dev/null differ diff --git a/core/assets/dontcopy.png b/core/assets/dontcopy.png deleted file mode 100644 index 4a1480f22b..0000000000 Binary files a/core/assets/dontcopy.png and /dev/null differ diff --git a/core/assets/homescreen_model_1.png b/core/assets/homescreen_model_1.png deleted file mode 100644 index d115ec7cfc..0000000000 Binary files a/core/assets/homescreen_model_1.png and /dev/null differ diff --git a/core/assets/homescreen_model_r.png b/core/assets/homescreen_model_r.png deleted file mode 100644 index 2f2cc72bdd..0000000000 Binary files a/core/assets/homescreen_model_r.png and /dev/null differ diff --git a/core/assets/icon_tools.png b/core/assets/icon_tools.png deleted file mode 100644 index 30db60f9f5..0000000000 Binary files a/core/assets/icon_tools.png and /dev/null differ diff --git a/core/assets/icon_update.png b/core/assets/icon_update.png deleted file mode 100644 index 70119cc685..0000000000 Binary files a/core/assets/icon_update.png and /dev/null differ diff --git a/core/assets/icon_wipe.png b/core/assets/icon_wipe.png deleted file mode 100644 index 6a8909c272..0000000000 Binary files a/core/assets/icon_wipe.png and /dev/null differ diff --git a/core/assets/left.png b/core/assets/left.png deleted file mode 100644 index 9f50201810..0000000000 Binary files a/core/assets/left.png and /dev/null differ diff --git a/core/assets/lock-new.png b/core/assets/lock-new.png new file mode 100644 index 0000000000..6cb7246af6 Binary files /dev/null and b/core/assets/lock-new.png differ diff --git a/core/assets/lock.png b/core/assets/lock.png deleted file mode 100644 index e2df8e611e..0000000000 Binary files a/core/assets/lock.png and /dev/null differ diff --git a/core/assets/logo.jpg b/core/assets/logo.jpg new file mode 100644 index 0000000000..a3b4eecc99 Binary files /dev/null and b/core/assets/logo.jpg differ diff --git a/core/assets/magic.png b/core/assets/magic.png new file mode 100644 index 0000000000..a7e3d28c6f Binary files /dev/null and b/core/assets/magic.png differ diff --git a/core/assets/receive.png b/core/assets/receive.png deleted file mode 100644 index f398b35db6..0000000000 Binary files a/core/assets/receive.png and /dev/null differ diff --git a/core/assets/recovery-old.png b/core/assets/recovery-old.png deleted file mode 100644 index dfa41cbc26..0000000000 Binary files a/core/assets/recovery-old.png and /dev/null differ diff --git a/core/assets/recovery.png b/core/assets/recovery.png deleted file mode 100644 index 40ac64a31e..0000000000 Binary files a/core/assets/recovery.png and /dev/null differ diff --git a/core/assets/send.png b/core/assets/send.png deleted file mode 100644 index a73d54125b..0000000000 Binary files a/core/assets/send.png and /dev/null differ diff --git a/core/assets/small-arrow.png b/core/assets/small-arrow.png deleted file mode 100644 index 26a80b8724..0000000000 Binary files a/core/assets/small-arrow.png and /dev/null differ diff --git a/core/assets/swipedown.png b/core/assets/swipedown.png deleted file mode 100644 index 00af05f40c..0000000000 Binary files a/core/assets/swipedown.png and /dev/null differ diff --git a/core/assets/trezor_lock.png b/core/assets/trezor_lock.png deleted file mode 100644 index cf43a2853c..0000000000 Binary files a/core/assets/trezor_lock.png and /dev/null differ diff --git a/core/assets/trezor_logo.png b/core/assets/trezor_logo.png deleted file mode 100644 index 06a5b3ca15..0000000000 Binary files a/core/assets/trezor_logo.png and /dev/null differ diff --git a/core/assets/wrong.png b/core/assets/wrong.png deleted file mode 100644 index 2a1061cd84..0000000000 Binary files a/core/assets/wrong.png and /dev/null differ diff --git a/core/embed/boardloader/.changelog.d/2623.added b/core/embed/boardloader/.changelog.d/2623.added new file mode 100644 index 0000000000..fe8e5b8cb6 --- /dev/null +++ b/core/embed/boardloader/.changelog.d/2623.added @@ -0,0 +1 @@ +Check image model when replacing bootloader diff --git a/core/embed/boardloader/main.c b/core/embed/boardloader/main.c index e17896b1d3..93f85b9579 100644 --- a/core/embed/boardloader/main.c +++ b/core/embed/boardloader/main.c @@ -53,14 +53,8 @@ struct BoardCapabilities capablities __attribute__((section(".capabilities_section"))) = { .header = CAPABILITIES_HEADER, .model_tag = MODEL_NAME, - .model_length = MODEL_NAME_MAX_LENGTH, -#if defined TREZOR_MODEL_T - .model_name = "TREZORT", -#elif defined TREZOR_MODEL_R - .model_name = "TREZORR", -#else -#error Unknown model -#endif + .model_length = sizeof(uint32_t), + .model_name = HW_MODEL, .version_tag = BOARDLOADER_VERSION, .version_length = sizeof(struct BoardloaderVersion), .version = {.version_major = VERSION_MAJOR, @@ -93,17 +87,29 @@ static uint32_t check_sdcard(void) { sdcard_power_off(); - image_header hdr; + if (sectrue == read_status) { + const image_header *hdr = + read_image_header((const uint8_t *)sdcard_buf, BOOTLOADER_IMAGE_MAGIC, + BOOTLOADER_IMAGE_MAXSIZE); - if ((sectrue == read_status) && - (sectrue == - load_image_header((const uint8_t *)sdcard_buf, BOOTLOADER_IMAGE_MAGIC, - BOOTLOADER_IMAGE_MAXSIZE, BOARDLOADER_KEY_M, - BOARDLOADER_KEY_N, BOARDLOADER_KEYS, &hdr))) { - return hdr.codelen; - } else { - return 0; + if (hdr != (const image_header *)sdcard_buf) { + return 0; + } + + if (sectrue != check_image_model(hdr)) { + return 0; + } + + if (sectrue != check_image_header_sig(hdr, BOARDLOADER_KEY_M, + BOARDLOADER_KEY_N, + BOARDLOADER_KEYS)) { + return 0; + } + + return hdr->codelen; } + + return 0; } static void progress_callback(int pos, int len) { display_printf("."); } @@ -220,18 +226,21 @@ int main(void) { } #endif - image_header hdr; + const image_header *hdr = + read_image_header((const uint8_t *)BOOTLOADER_START, + BOOTLOADER_IMAGE_MAGIC, BOOTLOADER_IMAGE_MAXSIZE); - ensure(load_image_header((const uint8_t *)BOOTLOADER_START, - BOOTLOADER_IMAGE_MAGIC, BOOTLOADER_IMAGE_MAXSIZE, - BOARDLOADER_KEY_M, BOARDLOADER_KEY_N, - BOARDLOADER_KEYS, &hdr), + ensure(hdr == (const image_header *)BOOTLOADER_START ? sectrue : secfalse, "invalid bootloader header"); + ensure(check_image_header_sig(hdr, BOARDLOADER_KEY_M, BOARDLOADER_KEY_N, + BOARDLOADER_KEYS), + "invalid bootloader signature"); + const uint8_t sectors[] = { FLASH_SECTOR_BOOTLOADER, }; - ensure(check_image_contents(&hdr, IMAGE_HEADER_SIZE, sectors, 1), + ensure(check_image_contents(hdr, IMAGE_HEADER_SIZE, sectors, 1), "invalid bootloader hash"); ensure_compatible_settings(); diff --git a/core/embed/bootloader/.changelog.d/2623.added b/core/embed/bootloader/.changelog.d/2623.added new file mode 100644 index 0000000000..133dcc6d1c --- /dev/null +++ b/core/embed/bootloader/.changelog.d/2623.added @@ -0,0 +1 @@ +Add model info to image and check when installing/running firmware diff --git a/core/embed/bootloader/bootui.c b/core/embed/bootloader/bootui.c index 8c6618b935..de83047e3c 100644 --- a/core/embed/bootloader/bootui.c +++ b/core/embed/bootloader/bootui.c @@ -215,31 +215,6 @@ void ui_screen_firmware_info(const vendor_header *const vhdr, display_refresh(); } -void ui_screen_firmware_fingerprint(const image_header *const hdr) { - display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_BL_BG); - display_text(16, 32, "Firmware fingerprint", -1, FONT_NORMAL, COLOR_BL_FG, - COLOR_BL_BG); - display_bar(16, 44, DISPLAY_RESX - 14 * 2, 1, COLOR_BL_FG); - - static const char *hexdigits = "0123456789abcdef"; - char fingerprint_str[64]; - for (int i = 0; i < 32; i++) { - fingerprint_str[i * 2] = hexdigits[(hdr->fingerprint[i] >> 4) & 0xF]; - fingerprint_str[i * 2 + 1] = hexdigits[hdr->fingerprint[i] & 0xF]; - } - for (int i = 0; i < 4; i++) { - display_text_center(120, 70 + i * 25, fingerprint_str + i * 16, 16, - FONT_MONO, COLOR_BL_FG, COLOR_BL_BG); - } - - display_bar_radius(9, 184, 222, 50, COLOR_BL_DONE, COLOR_BL_BG, 4); - display_icon(9 + (222 - 19) / 2, 184 + (50 - 16) / 2, 20, 16, - toi_icon_confirm + 12, sizeof(toi_icon_confirm) - 12, - COLOR_BL_BG, COLOR_BL_DONE); - PIXELDATA_DIRTY(); - display_refresh(); -} - // install UI void ui_screen_install_confirm_upgrade(const vendor_header *const vhdr, diff --git a/core/embed/bootloader/bootui.h b/core/embed/bootloader/bootui.h index 0d74d316ec..73285e419e 100644 --- a/core/embed/bootloader/bootui.h +++ b/core/embed/bootloader/bootui.h @@ -34,7 +34,6 @@ void ui_screen_welcome_third(void); void ui_screen_firmware_info(const vendor_header* const vhdr, const image_header* const hdr); -void ui_screen_firmware_fingerprint(const image_header* const hdr); void ui_screen_install_confirm_upgrade(const vendor_header* const vhdr, const image_header* const hdr); diff --git a/core/embed/bootloader/header.S b/core/embed/bootloader/header.S index 3e3849a670..9014961835 100644 --- a/core/embed/bootloader/header.S +++ b/core/embed/bootloader/header.S @@ -10,7 +10,11 @@ g_header: .byte 'T','R','Z','B' // magic .word g_header_end - g_header // hdrlen +#ifdef TREZOR_MODEL_T .word 0 // expiry +#else + .word 1 // expiry +#endif .word _codelen // codelen .byte VERSION_MAJOR // vmajor .byte VERSION_MINOR // vminor @@ -20,7 +24,10 @@ g_header: .byte FIX_VERSION_MINOR // fix_vminor .byte FIX_VERSION_PATCH // fix_vpatch .byte FIX_VERSION_BUILD // fix_vbuild - . = . + 8 // reserved + .word HW_MODEL // type of the designated hardware + .byte HW_REVISION // revision of the designated hardware + .byte VERSION_MONOTONIC // monotonic version of the binary + . = . + 2 // reserved . = . + 512 // hash1 ... hash16 . = . + 415 // reserved .byte 0 // sigmask diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index 0e8e6701f0..c9f5c5ba69 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -159,7 +159,7 @@ static secbool bootloader_usb_loop(const vendor_header *const vhdr, break; case 7: // FirmwareUpload r = process_msg_FirmwareUpload(USB_IFACE_NUM, msg_size, buf); - if (r < 0 && r != -4) { // error, but not user abort (-4) + if (r < 0 && r != UPLOAD_ERR_USER_ABORT) { // error, but not user abort ui_fadeout(); ui_screen_fail(); ui_fadein(); @@ -193,10 +193,9 @@ static secbool bootloader_usb_loop(const vendor_header *const vhdr, } } -secbool load_vendor_header_keys(const uint8_t *const data, - vendor_header *const vhdr) { - return load_vendor_header(data, BOOTLOADER_KEY_M, BOOTLOADER_KEY_N, - BOOTLOADER_KEYS, vhdr); +secbool check_vendor_header_keys(const vendor_header *const vhdr) { + return check_vendor_header_sig(vhdr, BOOTLOADER_KEY_M, BOOTLOADER_KEY_N, + BOOTLOADER_KEYS); } static secbool check_vendor_header_lock(const vendor_header *const vhdr) { @@ -303,23 +302,40 @@ int main(void) { } #endif + const image_header *hdr = NULL; vendor_header vhdr; - image_header hdr; - // detect whether the devices contains a valid firmware + // detect whether the device contains a valid firmware + secbool firmware_present = sectrue; + + if (sectrue != read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr)) { + firmware_present = secfalse; + } + + if (sectrue == firmware_present) { + firmware_present = check_vendor_header_keys(&vhdr); + } - secbool firmware_present = - load_vendor_header_keys((const uint8_t *)FIRMWARE_START, &vhdr); if (sectrue == firmware_present) { firmware_present = check_vendor_header_lock(&vhdr); } + if (sectrue == firmware_present) { - firmware_present = load_image_header( - (const uint8_t *)(FIRMWARE_START + vhdr.hdrlen), FIRMWARE_IMAGE_MAGIC, - FIRMWARE_IMAGE_MAXSIZE, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub, &hdr); + hdr = read_image_header((const uint8_t *)(FIRMWARE_START + vhdr.hdrlen), + FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE); + if (hdr != (const image_header *)(FIRMWARE_START + vhdr.hdrlen)) { + firmware_present = secfalse; + } + } + if (sectrue == firmware_present) { + firmware_present = check_image_model(hdr); } if (sectrue == firmware_present) { firmware_present = - check_image_contents(&hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, + check_image_header_sig(hdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub); + } + if (sectrue == firmware_present) { + firmware_present = + check_image_contents(hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, FIRMWARE_SECTORS, FIRMWARE_SECTORS_COUNT); } @@ -357,26 +373,35 @@ int main(void) { // ... or we have stay_in_bootloader flag to force it if (touched || stay_in_bootloader == sectrue) { // no ui_fadeout(); - we already start from black screen - ui_screen_firmware_info(&vhdr, &hdr); + ui_screen_firmware_info(&vhdr, hdr); ui_fadein(); // and start the usb loop - if (bootloader_usb_loop(&vhdr, &hdr) != sectrue) { + if (bootloader_usb_loop(&vhdr, hdr) != sectrue) { return 1; } } - ensure(load_vendor_header_keys((const uint8_t *)FIRMWARE_START, &vhdr), + ensure(read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr), "invalid vendor header"); + ensure(check_vendor_header_keys(&vhdr), "invalid vendor header signature"); + ensure(check_vendor_header_lock(&vhdr), "unauthorized vendor keys"); - ensure(load_image_header((const uint8_t *)(FIRMWARE_START + vhdr.hdrlen), - FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE, - vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub, &hdr), + hdr = read_image_header((const uint8_t *)(FIRMWARE_START + vhdr.hdrlen), + FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE); + + ensure(hdr == (const image_header *)(FIRMWARE_START + vhdr.hdrlen) ? sectrue + : secfalse, "invalid firmware header"); - ensure(check_image_contents(&hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, + ensure(check_image_model(hdr), "wrong firmware model"); + + ensure(check_image_header_sig(hdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub), + "invalid firmware signature"); + + ensure(check_image_contents(hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, FIRMWARE_SECTORS, FIRMWARE_SECTORS_COUNT), "invalid firmware hash"); @@ -384,7 +409,7 @@ int main(void) { if ((vhdr.vtrust & VTRUST_ALL) != VTRUST_ALL) { // ui_fadeout(); // no fadeout - we start from black screen - ui_screen_boot(&vhdr, &hdr); + ui_screen_boot(&vhdr, hdr); ui_fadein(); int delay = (vhdr.vtrust & VTRUST_WAIT) ^ VTRUST_WAIT; diff --git a/core/embed/bootloader/messages.c b/core/embed/bootloader/messages.c index 14474f1c1a..eab9c771b9 100644 --- a/core/embed/bootloader/messages.c +++ b/core/embed/bootloader/messages.c @@ -403,8 +403,7 @@ static bool _read_payload(pb_istream_t *stream, const pb_field_t *field, return true; } -secbool load_vendor_header_keys(const uint8_t *const data, - vendor_header *const vhdr); +secbool check_vendor_header_keys(const vendor_header *const vhdr); static int version_compare(uint32_t vera, uint32_t verb) { int a, b; @@ -422,8 +421,8 @@ static int version_compare(uint32_t vera, uint32_t verb) { return a - b; } -static void detect_installation(vendor_header *current_vhdr, - image_header *current_hdr, +static void detect_installation(const vendor_header *current_vhdr, + const image_header *current_hdr, const vendor_header *const new_vhdr, const image_header *const new_hdr, secbool *is_new, secbool *is_upgrade, @@ -431,16 +430,17 @@ static void detect_installation(vendor_header *current_vhdr, *is_new = secfalse; *is_upgrade = secfalse; *is_downgrade_wipe = secfalse; - if (sectrue != - load_vendor_header_keys((const uint8_t *)FIRMWARE_START, current_vhdr)) { + if (sectrue != check_vendor_header_keys(current_vhdr)) { *is_new = sectrue; return; } - if (sectrue != - load_image_header((const uint8_t *)FIRMWARE_START + current_vhdr->hdrlen, - FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE, - current_vhdr->vsig_m, current_vhdr->vsig_n, - current_vhdr->vpub, current_hdr)) { + if (sectrue != check_image_model(current_hdr)) { + *is_new = sectrue; + return; + } + if (sectrue != check_image_header_sig(current_hdr, current_vhdr->vsig_m, + current_vhdr->vsig_n, + current_vhdr->vpub)) { *is_new = sectrue; return; } @@ -472,7 +472,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid chunk size"); MSG_SEND(Failure); - return -1; + return UPLOAD_ERR_INVALID_CHUNK_SIZE; } static image_header hdr; @@ -483,29 +483,81 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, if (headers_offset == 0) { // first block and headers are not yet parsed vendor_header vhdr; - if (sectrue != load_vendor_header_keys(CHUNK_BUFFER_PTR, &vhdr)) { + + if (sectrue != read_vendor_header(CHUNK_BUFFER_PTR, &vhdr)) { MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header"); MSG_SEND(Failure); - return -2; + return UPLOAD_ERR_INVALID_VENDOR_HEADER; } - if (sectrue != load_image_header(CHUNK_BUFFER_PTR + vhdr.hdrlen, - FIRMWARE_IMAGE_MAGIC, - FIRMWARE_IMAGE_MAXSIZE, vhdr.vsig_m, - vhdr.vsig_n, vhdr.vpub, &hdr)) { + + if (sectrue != check_vendor_header_keys(&vhdr)) { + MSG_SEND_INIT(Failure); + MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); + MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header signature"); + MSG_SEND(Failure); + return UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG; + } + + const image_header *received_hdr = + read_image_header(CHUNK_BUFFER_PTR + vhdr.hdrlen, + FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE); + + if (received_hdr != + (const image_header *)(CHUNK_BUFFER_PTR + vhdr.hdrlen)) { MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid firmware header"); MSG_SEND(Failure); - return -3; + return UPLOAD_ERR_INVALID_IMAGE_HEADER; } + if (sectrue != check_image_model(received_hdr)) { + MSG_SEND_INIT(Failure); + MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); + MSG_SEND_ASSIGN_STRING(message, "Wrong firmware model"); + MSG_SEND(Failure); + return UPLOAD_ERR_INVALID_IMAGE_MODEL; + } + + if (sectrue != check_image_header_sig(received_hdr, vhdr.vsig_m, + vhdr.vsig_n, vhdr.vpub)) { + MSG_SEND_INIT(Failure); + MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); + MSG_SEND_ASSIGN_STRING(message, "Invalid firmware signature"); + MSG_SEND(Failure); + return UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG; + } + + memcpy(&hdr, received_hdr, sizeof(hdr)); + vendor_header current_vhdr; - image_header current_hdr; + secbool is_new = secfalse; - detect_installation(¤t_vhdr, ¤t_hdr, &vhdr, &hdr, &is_new, - &is_upgrade, &is_downgrade_wipe); + + if (sectrue != + read_vendor_header((const uint8_t *)FIRMWARE_START, ¤t_vhdr)) { + is_new = sectrue; + } + + const image_header *current_hdr = NULL; + + if (is_new == secfalse) { + current_hdr = read_image_header( + (const uint8_t *)FIRMWARE_START + current_vhdr.hdrlen, + FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE); + + if (current_hdr != + (const image_header *)(FIRMWARE_START + current_vhdr.hdrlen)) { + is_new = sectrue; + } + } + + if (is_new == secfalse) { + detect_installation(¤t_vhdr, current_hdr, &vhdr, &hdr, &is_new, + &is_upgrade, &is_downgrade_wipe); + } int response = INPUT_CANCEL; if (sectrue == is_new) { @@ -528,10 +580,10 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, if (INPUT_CANCEL == response) { ui_fadeout(); - ui_screen_firmware_info(¤t_vhdr, ¤t_hdr); + ui_screen_firmware_info(¤t_vhdr, current_hdr); ui_fadein(); send_user_abort(iface_num, "Firmware install cancelled"); - return -4; + return UPLOAD_ERR_USER_ABORT; } headers_offset = IMAGE_HEADER_SIZE + vhdr.hdrlen; @@ -572,7 +624,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Firmware too big"); MSG_SEND(Failure); - return -5; + return UPLOAD_ERR_FIRMWARE_TOO_BIG; } if (sectrue != check_single_hash(hdr.hashes + firmware_block * 32, @@ -591,7 +643,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid chunk hash"); MSG_SEND(Failure); - return -6; + return UPLOAD_ERR_INVALID_CHUNK_HASH; } ensure(flash_unlock_write(), NULL); @@ -654,11 +706,11 @@ int process_msg_WipeDevice(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Could not erase flash"); MSG_SEND(Failure); - return -1; + return WIPE_ERR_CANNOT_ERASE; } else { MSG_SEND_INIT(Success); MSG_SEND(Success); - return 0; + return WIPE_OK; } } diff --git a/core/embed/bootloader/messages.h b/core/embed/bootloader/messages.h index fd11b82ac7..0937f60627 100644 --- a/core/embed/bootloader/messages.h +++ b/core/embed/bootloader/messages.h @@ -29,6 +29,24 @@ #define FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT 2 +enum { + UPLOAD_OK = 0, + UPLOAD_ERR_INVALID_CHUNK_SIZE = -1, + UPLOAD_ERR_INVALID_VENDOR_HEADER = -2, + UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG = -3, + UPLOAD_ERR_INVALID_IMAGE_HEADER = -4, + UPLOAD_ERR_INVALID_IMAGE_MODEL = -5, + UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG = -6, + UPLOAD_ERR_USER_ABORT = -7, + UPLOAD_ERR_FIRMWARE_TOO_BIG = -8, + UPLOAD_ERR_INVALID_CHUNK_HASH = -9, +}; + +enum { + WIPE_OK = 0, + WIPE_ERR_CANNOT_ERASE = -1, +}; + secbool msg_parse_header(const uint8_t *buf, uint16_t *msg_id, uint32_t *msg_size); diff --git a/core/embed/bootloader_ci/.changelog.d/2623.added b/core/embed/bootloader_ci/.changelog.d/2623.added new file mode 100644 index 0000000000..133dcc6d1c --- /dev/null +++ b/core/embed/bootloader_ci/.changelog.d/2623.added @@ -0,0 +1 @@ +Add model info to image and check when installing/running firmware diff --git a/core/embed/bootloader_ci/header.S b/core/embed/bootloader_ci/header.S index 3e3849a670..bb386ef7fd 100644 --- a/core/embed/bootloader_ci/header.S +++ b/core/embed/bootloader_ci/header.S @@ -10,7 +10,11 @@ g_header: .byte 'T','R','Z','B' // magic .word g_header_end - g_header // hdrlen +#ifdef TREZOR_MODEL_T .word 0 // expiry +#else + .word 1 // expiry +#endif .word _codelen // codelen .byte VERSION_MAJOR // vmajor .byte VERSION_MINOR // vminor @@ -20,7 +24,10 @@ g_header: .byte FIX_VERSION_MINOR // fix_vminor .byte FIX_VERSION_PATCH // fix_vpatch .byte FIX_VERSION_BUILD // fix_vbuild - . = . + 8 // reserved + .word HW_MODEL // type of the designated hardware + .byte HW_REVISION // revision of the designated hardware + .byte VERSION_MONOTONIC // monotonic version + . = . + 2 // reserved . = . + 512 // hash1 ... hash16 . = . + 415 // reserved .byte 0 // sigmask diff --git a/core/embed/bootloader_ci/main.c b/core/embed/bootloader_ci/main.c index 4f10468f4e..32baff7de2 100644 --- a/core/embed/bootloader_ci/main.c +++ b/core/embed/bootloader_ci/main.c @@ -134,7 +134,7 @@ static secbool bootloader_usb_loop(const vendor_header *const vhdr, break; case 7: // FirmwareUpload r = process_msg_FirmwareUpload(USB_IFACE_NUM, msg_size, buf); - if (r < 0 && r != -4) { // error, but not user abort (-4) + if (r < 0 && r != UPLOAD_ERR_USER_ABORT) { // error, but not user abort ui_screen_fail(); usb_stop(); usb_deinit(); @@ -163,10 +163,9 @@ static secbool bootloader_usb_loop(const vendor_header *const vhdr, } } -secbool load_vendor_header_keys(const uint8_t *const data, - vendor_header *const vhdr) { - return load_vendor_header(data, BOOTLOADER_KEY_M, BOOTLOADER_KEY_N, - BOOTLOADER_KEYS, vhdr); +secbool check_vendor_header_keys(vendor_header *const vhdr) { + return check_vendor_header_sig(vhdr, BOOTLOADER_KEY_M, BOOTLOADER_KEY_N, + BOOTLOADER_KEYS); } static secbool check_vendor_header_lock(const vendor_header *const vhdr) { @@ -227,24 +226,40 @@ int main(void) { display_clear(); + const image_header *hdr = NULL; vendor_header vhdr; - image_header hdr; + // detect whether the device contains a valid firmware + secbool firmware_present = sectrue; - // detect whether the devices contains a valid firmware + if (sectrue != read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr)) { + firmware_present = secfalse; + } + + if (sectrue == firmware_present) { + firmware_present = check_vendor_header_keys(&vhdr); + } - secbool firmware_present = - load_vendor_header_keys((const uint8_t *)FIRMWARE_START, &vhdr); if (sectrue == firmware_present) { firmware_present = check_vendor_header_lock(&vhdr); } + if (sectrue == firmware_present) { - firmware_present = load_image_header( - (const uint8_t *)(FIRMWARE_START + vhdr.hdrlen), FIRMWARE_IMAGE_MAGIC, - FIRMWARE_IMAGE_MAXSIZE, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub, &hdr); + hdr = read_image_header((const uint8_t *)(FIRMWARE_START + vhdr.hdrlen), + FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE); + if (hdr != (const image_header *)(FIRMWARE_START + vhdr.hdrlen)) { + firmware_present = secfalse; + } + } + if (sectrue == firmware_present) { + firmware_present = check_image_model(hdr); } if (sectrue == firmware_present) { firmware_present = - check_image_contents(&hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, + check_image_header_sig(hdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub); + } + if (sectrue == firmware_present) { + firmware_present = + check_image_contents(hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, FIRMWARE_SECTORS, FIRMWARE_SECTORS_COUNT); } @@ -260,17 +275,26 @@ int main(void) { return 1; } - ensure(load_vendor_header_keys((const uint8_t *)FIRMWARE_START, &vhdr), + ensure(read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr), "invalid vendor header"); + ensure(check_vendor_header_keys(&vhdr), "invalid vendor header signature"); + ensure(check_vendor_header_lock(&vhdr), "unauthorized vendor keys"); - ensure(load_image_header((const uint8_t *)(FIRMWARE_START + vhdr.hdrlen), - FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE, - vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub, &hdr), + hdr = read_image_header((const uint8_t *)(FIRMWARE_START + vhdr.hdrlen), + FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE); + + ensure(hdr == (const image_header *)(FIRMWARE_START + vhdr.hdrlen) ? sectrue + : secfalse, "invalid firmware header"); - ensure(check_image_contents(&hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, + ensure(check_image_model(hdr), "wrong firmware model"); + + ensure(check_image_header_sig(hdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub), + "invalid firmware signature"); + + ensure(check_image_contents(hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, FIRMWARE_SECTORS, FIRMWARE_SECTORS_COUNT), "invalid firmware hash"); diff --git a/core/embed/bootloader_ci/messages.c b/core/embed/bootloader_ci/messages.c index a024874045..c2e0c92e6a 100644 --- a/core/embed/bootloader_ci/messages.c +++ b/core/embed/bootloader_ci/messages.c @@ -401,8 +401,7 @@ static bool _read_payload(pb_istream_t *stream, const pb_field_t *field, return true; } -secbool load_vendor_header_keys(const uint8_t *const data, - vendor_header *const vhdr); +secbool check_vendor_header_keys(const vendor_header *const vhdr); static int version_compare(uint32_t vera, uint32_t verb) { int a, b; @@ -420,8 +419,8 @@ static int version_compare(uint32_t vera, uint32_t verb) { return a - b; } -static void detect_installation(vendor_header *current_vhdr, - image_header *current_hdr, +static void detect_installation(const vendor_header *current_vhdr, + const image_header *current_hdr, const vendor_header *const new_vhdr, const image_header *const new_hdr, secbool *is_new, secbool *is_upgrade, @@ -429,16 +428,17 @@ static void detect_installation(vendor_header *current_vhdr, *is_new = secfalse; *is_upgrade = secfalse; *is_downgrade_wipe = secfalse; - if (sectrue != - load_vendor_header_keys((const uint8_t *)FIRMWARE_START, current_vhdr)) { + if (sectrue != check_vendor_header_keys(current_vhdr)) { *is_new = sectrue; return; } - if (sectrue != - load_image_header((const uint8_t *)FIRMWARE_START + current_vhdr->hdrlen, - FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE, - current_vhdr->vsig_m, current_vhdr->vsig_n, - current_vhdr->vpub, current_hdr)) { + if (sectrue != check_image_model(current_hdr)) { + *is_new = sectrue; + return; + } + if (sectrue != check_image_header_sig(current_hdr, current_vhdr->vsig_m, + current_vhdr->vsig_n, + current_vhdr->vpub)) { *is_new = sectrue; return; } @@ -470,7 +470,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid chunk size"); MSG_SEND(Failure); - return -1; + return UPLOAD_ERR_INVALID_CHUNK_SIZE; } static image_header hdr; @@ -481,29 +481,80 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, if (headers_offset == 0) { // first block and headers are not yet parsed vendor_header vhdr; - if (sectrue != load_vendor_header_keys(chunk_buffer, &vhdr)) { + + if (sectrue != read_vendor_header(chunk_buffer, &vhdr)) { MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header"); MSG_SEND(Failure); - return -2; + return UPLOAD_ERR_INVALID_VENDOR_HEADER; } - if (sectrue != load_image_header(chunk_buffer + vhdr.hdrlen, - FIRMWARE_IMAGE_MAGIC, - FIRMWARE_IMAGE_MAXSIZE, vhdr.vsig_m, - vhdr.vsig_n, vhdr.vpub, &hdr)) { + + if (sectrue != check_vendor_header_keys(&vhdr)) { + MSG_SEND_INIT(Failure); + MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); + MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header signature"); + MSG_SEND(Failure); + return UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG; + } + + const image_header *received_hdr = + read_image_header(chunk_buffer + vhdr.hdrlen, FIRMWARE_IMAGE_MAGIC, + FIRMWARE_IMAGE_MAXSIZE); + + if (received_hdr != (const image_header *)chunk_buffer + vhdr.hdrlen) { MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid firmware header"); MSG_SEND(Failure); - return -3; + return UPLOAD_ERR_INVALID_IMAGE_HEADER; } + if (sectrue != check_image_model(received_hdr)) { + MSG_SEND_INIT(Failure); + MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); + MSG_SEND_ASSIGN_STRING(message, "Wrong firmware model"); + MSG_SEND(Failure); + return UPLOAD_ERR_INVALID_IMAGE_MODEL; + } + + if (sectrue != check_image_header_sig(received_hdr, vhdr.vsig_m, + vhdr.vsig_n, vhdr.vpub)) { + MSG_SEND_INIT(Failure); + MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); + MSG_SEND_ASSIGN_STRING(message, "Invalid firmware signature"); + MSG_SEND(Failure); + return UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG; + } + + memcpy(&hdr, received_hdr, sizeof(hdr)); + vendor_header current_vhdr; - image_header current_hdr; + secbool is_new = secfalse; - detect_installation(¤t_vhdr, ¤t_hdr, &vhdr, &hdr, &is_new, - &is_upgrade, &is_downgrade_wipe); + + if (sectrue != + read_vendor_header((const uint8_t *)FIRMWARE_START, ¤t_vhdr)) { + is_new = sectrue; + } + + const image_header *current_hdr = NULL; + + if (is_new == secfalse) { + current_hdr = read_image_header( + (const uint8_t *)FIRMWARE_START + current_vhdr.hdrlen, + FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE); + + if (current_hdr != + (const image_header *)(FIRMWARE_START + current_vhdr.hdrlen)) { + is_new = sectrue; + } + } + + if (is_new == secfalse) { + detect_installation(¤t_vhdr, current_hdr, &vhdr, &hdr, &is_new, + &is_upgrade, &is_downgrade_wipe); + } // no user confirmations, go directly to upload @@ -539,7 +590,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Firmware too big"); MSG_SEND(Failure); - return -5; + return UPLOAD_ERR_FIRMWARE_TOO_BIG; } if (sectrue != check_single_hash(hdr.hashes + firmware_block * 32, @@ -558,7 +609,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid chunk hash"); MSG_SEND(Failure); - return -6; + return UPLOAD_ERR_INVALID_CHUNK_HASH; } ensure(flash_unlock_write(), NULL); @@ -621,11 +672,11 @@ int process_msg_WipeDevice(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Could not erase flash"); MSG_SEND(Failure); - return -1; + return WIPE_ERR_CANNOT_ERASE; } else { MSG_SEND_INIT(Success); MSG_SEND(Success); - return 0; + return WIPE_OK; } } diff --git a/core/embed/bootloader_ci/messages.h b/core/embed/bootloader_ci/messages.h index fd11b82ac7..0937f60627 100644 --- a/core/embed/bootloader_ci/messages.h +++ b/core/embed/bootloader_ci/messages.h @@ -29,6 +29,24 @@ #define FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT 2 +enum { + UPLOAD_OK = 0, + UPLOAD_ERR_INVALID_CHUNK_SIZE = -1, + UPLOAD_ERR_INVALID_VENDOR_HEADER = -2, + UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG = -3, + UPLOAD_ERR_INVALID_IMAGE_HEADER = -4, + UPLOAD_ERR_INVALID_IMAGE_MODEL = -5, + UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG = -6, + UPLOAD_ERR_USER_ABORT = -7, + UPLOAD_ERR_FIRMWARE_TOO_BIG = -8, + UPLOAD_ERR_INVALID_CHUNK_HASH = -9, +}; + +enum { + WIPE_OK = 0, + WIPE_ERR_CANNOT_ERASE = -1, +}; + secbool msg_parse_header(const uint8_t *buf, uint16_t *msg_id, uint32_t *msg_size); diff --git a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip39.h b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip39.h index cfca37c4f4..fe731fdcc1 100644 --- a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip39.h +++ b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip39.h @@ -25,41 +25,6 @@ /// package: trezorcrypto.bip39 -/// def complete_word(prefix: str) -> str | None: -/// """ -/// Return the first word from the wordlist starting with prefix. -/// """ -STATIC mp_obj_t mod_trezorcrypto_bip39_complete_word(mp_obj_t prefix) { - mp_buffer_info_t pfx = {0}; - mp_get_buffer_raise(prefix, &pfx, MP_BUFFER_READ); - if (pfx.len == 0) { - return mp_const_none; - } - const char *word = mnemonic_complete_word(pfx.buf, pfx.len); - if (word) { - return mp_obj_new_str(word, strlen(word)); - } else { - return mp_const_none; - } -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_bip39_complete_word_obj, - mod_trezorcrypto_bip39_complete_word); - -/// def word_completion_mask(prefix: str) -> int: -/// """ -/// Return possible 1-letter suffixes for given word prefix. -/// Result is a bitmask, with 'a' on the lowest bit, 'b' on the second -/// lowest, etc. -/// """ -STATIC mp_obj_t mod_trezorcrypto_bip39_word_completion_mask(mp_obj_t prefix) { - mp_buffer_info_t pfx = {0}; - mp_get_buffer_raise(prefix, &pfx, MP_BUFFER_READ); - return mp_obj_new_int(mnemonic_word_completion_mask(pfx.buf, pfx.len)); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1( - mod_trezorcrypto_bip39_word_completion_mask_obj, - mod_trezorcrypto_bip39_word_completion_mask); - /// def generate(strength: int) -> str: /// """ /// Generate a mnemonic of given strength (128, 160, 192, 224 and 256 bits). @@ -148,10 +113,6 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorcrypto_bip39_seed_obj, 2, STATIC const mp_rom_map_elem_t mod_trezorcrypto_bip39_globals_table[] = { {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bip39)}, - {MP_ROM_QSTR(MP_QSTR_complete_word), - MP_ROM_PTR(&mod_trezorcrypto_bip39_complete_word_obj)}, - {MP_ROM_QSTR(MP_QSTR_word_completion_mask), - MP_ROM_PTR(&mod_trezorcrypto_bip39_word_completion_mask_obj)}, {MP_ROM_QSTR(MP_QSTR_generate), MP_ROM_PTR(&mod_trezorcrypto_bip39_generate_obj)}, {MP_ROM_QSTR(MP_QSTR_from_data), diff --git a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-slip39.h b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-slip39.h index b3341066e5..1633392f15 100644 --- a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-slip39.h +++ b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-slip39.h @@ -24,47 +24,6 @@ /// package: trezorcrypto.slip39 -/// def word_completion_mask(prefix: int) -> int: -/// """ -/// Calculates which buttons still can be pressed after some already were. -/// Returns a 9-bit bitmask, where each bit specifies which buttons -/// can be further pressed (there are still words in this combination). -/// LSB denotes first button. -/// -/// Example: 110000110 - second, third, eighth and ninth button still can be -/// pressed. -/// """ -STATIC mp_obj_t mod_trezorcrypto_slip39_word_completion_mask(mp_obj_t _prefix) { - uint16_t prefix = mp_obj_get_int(_prefix); - - if (prefix < 1 || prefix > 9999) { - mp_raise_ValueError( - "Invalid button prefix (range between 1 and 9999 is allowed)"); - } - return mp_obj_new_int_from_uint(slip39_word_completion_mask(prefix)); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1( - mod_trezorcrypto_slip39_word_completion_mask_obj, - mod_trezorcrypto_slip39_word_completion_mask); - -/// def button_sequence_to_word(prefix: int) -> str: -/// """ -/// Finds the first word that fits the given button prefix. -/// """ -STATIC mp_obj_t -mod_trezorcrypto_slip39_button_sequence_to_word(mp_obj_t _prefix) { - uint16_t prefix = mp_obj_get_int(_prefix); - - const char *word = button_sequence_to_word(prefix); - if (word == NULL) { - mp_raise_ValueError("Invalid button prefix"); - } - return mp_obj_new_str_copy(&mp_type_str, (const uint8_t *)word, strlen(word)); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1( - mod_trezorcrypto_slip39_button_sequence_to_word_obj, - mod_trezorcrypto_slip39_button_sequence_to_word); - /// def word_index(word: str) -> int: /// """ /// Finds index of given word. @@ -104,10 +63,6 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_slip39_get_word_obj, STATIC const mp_rom_map_elem_t mod_trezorcrypto_slip39_globals_table[] = { {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_slip39)}, - {MP_ROM_QSTR(MP_QSTR_word_completion_mask), - MP_ROM_PTR(&mod_trezorcrypto_slip39_word_completion_mask_obj)}, - {MP_ROM_QSTR(MP_QSTR_button_sequence_to_word), - MP_ROM_PTR(&mod_trezorcrypto_slip39_button_sequence_to_word_obj)}, {MP_ROM_QSTR(MP_QSTR_word_index), MP_ROM_PTR(&mod_trezorcrypto_slip39_word_index_obj)}, {MP_ROM_QSTR(MP_QSTR_get_word), diff --git a/core/embed/extmod/modtrezorio/ff.c b/core/embed/extmod/modtrezorio/ff.c index 415849999a..b48b70b30e 100644 --- a/core/embed/extmod/modtrezorio/ff.c +++ b/core/embed/extmod/modtrezorio/ff.c @@ -1,10 +1,10 @@ // clang-format off /*----------------------------------------------------------------------------/ -/ FatFs - Generic FAT Filesystem Module R0.14 / +/ FatFs - Generic FAT Filesystem Module R0.15 w/patch1 / /-----------------------------------------------------------------------------/ / -/ Copyright (C) 2019, ChaN, all right reserved. +/ Copyright (C) 2022, ChaN, all right reserved. / / FatFs module is an open source software. Redistribution and use of FatFs in / source and binary forms, with or without modification, are permitted provided @@ -21,6 +21,7 @@ /----------------------------------------------------------------------------*/ +#include #include "ff.h" /* Declarations of FatFs API */ #include "diskio.h" /* Declarations of device I/O functions */ @@ -31,7 +32,7 @@ ---------------------------------------------------------------------------*/ -#if FF_DEFINED != 86606 /* Revision ID */ +#if FF_DEFINED != 80286 /* Revision ID */ #error Wrong include file (ff.h). #endif @@ -49,6 +50,8 @@ #define IsUpper(c) ((c) >= 'A' && (c) <= 'Z') #define IsLower(c) ((c) >= 'a' && (c) <= 'z') #define IsDigit(c) ((c) >= '0' && (c) <= '9') +#define IsSeparator(c) ((c) == '/' || (c) == '\\') +#define IsTerminator(c) ((UINT)(c) < (FF_USE_LFN ? ' ' : '!')) #define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF) #define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF) #define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF) @@ -63,7 +66,8 @@ /* Additional file attribute bits for internal use */ #define AM_VOL 0x08 /* Volume label */ #define AM_LFN 0x0F /* LFN entry */ -#define AM_MASK 0x3F /* Mask of defined bits */ +#define AM_MASK 0x3F /* Mask of defined bits in FAT */ +#define AM_MASKX 0x37 /* Mask of defined bits in exFAT */ /* Name status flags in fn[11] */ @@ -206,26 +210,26 @@ #define PTE_StLba 8 /* MBR PTE: Start in LBA */ #define PTE_SizLba 12 /* MBR PTE: Size in LBA */ -#define GPTH_Sign 0 /* GPT: Header signature (8-byte) */ -#define GPTH_Rev 8 /* GPT: Revision (DWORD) */ -#define GPTH_Size 12 /* GPT: Header size (DWORD) */ -#define GPTH_Bcc 16 /* GPT: Header BCC (DWORD) */ -#define GPTH_CurLba 24 /* GPT: Main header LBA (QWORD) */ -#define GPTH_BakLba 32 /* GPT: Backup header LBA (QWORD) */ -#define GPTH_FstLba 40 /* GPT: First LBA for partitions (QWORD) */ -#define GPTH_LstLba 48 /* GPT: Last LBA for partitions (QWORD) */ -#define GPTH_DskGuid 56 /* GPT: Disk GUID (16-byte) */ -#define GPTH_PtOfs 72 /* GPT: Partation table LBA (QWORD) */ -#define GPTH_PtNum 80 /* GPT: Number of table entries (DWORD) */ -#define GPTH_PteSize 84 /* GPT: Size of table entry (DWORD) */ -#define GPTH_PtBcc 88 /* GPT: Partation table BCC (DWORD) */ -#define SZ_GPTE 128 /* GPT: Size of partition table entry */ +#define GPTH_Sign 0 /* GPT HDR: Signature (8-byte) */ +#define GPTH_Rev 8 /* GPT HDR: Revision (DWORD) */ +#define GPTH_Size 12 /* GPT HDR: Header size (DWORD) */ +#define GPTH_Bcc 16 /* GPT HDR: Header BCC (DWORD) */ +#define GPTH_CurLba 24 /* GPT HDR: This header LBA (QWORD) */ +#define GPTH_BakLba 32 /* GPT HDR: Another header LBA (QWORD) */ +#define GPTH_FstLba 40 /* GPT HDR: First LBA for partition data (QWORD) */ +#define GPTH_LstLba 48 /* GPT HDR: Last LBA for partition data (QWORD) */ +#define GPTH_DskGuid 56 /* GPT HDR: Disk GUID (16-byte) */ +#define GPTH_PtOfs 72 /* GPT HDR: Partition table LBA (QWORD) */ +#define GPTH_PtNum 80 /* GPT HDR: Number of table entries (DWORD) */ +#define GPTH_PteSize 84 /* GPT HDR: Size of table entry (DWORD) */ +#define GPTH_PtBcc 88 /* GPT HDR: Partition table BCC (DWORD) */ +#define SZ_GPTE 128 /* GPT PTE: Size of partition table entry */ #define GPTE_PtGuid 0 /* GPT PTE: Partition type GUID (16-byte) */ #define GPTE_UpGuid 16 /* GPT PTE: Partition unique GUID (16-byte) */ -#define GPTE_FstLba 32 /* GPT PTE: First LBA (QWORD) */ -#define GPTE_LstLba 40 /* GPT PTE: Last LBA inclusive (QWORD) */ -#define GPTE_Flags 48 /* GPT PTE: Flags (QWORD) */ -#define GPTE_Name 56 /* GPT PTE: Name */ +#define GPTE_FstLba 32 /* GPT PTE: First LBA of partition (QWORD) */ +#define GPTE_LstLba 40 /* GPT PTE: Last LBA of partition (QWORD) */ +#define GPTE_Flags 48 /* GPT PTE: Partition flags (QWORD) */ +#define GPTE_Name 56 /* GPT PTE: Partition name */ /* Post process on fatal error in the file operations */ @@ -238,7 +242,7 @@ /* Definitions of logical drive - physical location conversion */ #define LD2PD(vol) (BYTE)(vol) /* Each logical drive is associated with the same physical drive number */ -#define LD2PT(vol) 0 /* Find first valid partition or in SFD */ +#define LD2PT(vol) 0 /* Auto partition search */ /* Definitions of sector size */ @@ -524,23 +528,6 @@ static void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte word in little-end /* String functions */ /*-----------------------------------------------------------------------*/ -#include - -// These were originally provided by the FatFs library but we use externally -// provided versions from C stdlib to (hopefully) reduce code size and use -// more efficient versions. -#define mem_cpy memcpy -#define mem_set memset -#define mem_cmp memcmp - -/* Check if chr is contained in the string */ -static int chk_chr (const char* str, int chr) /* NZ:contained, ZR:not contained */ -{ - while (*str && *str != chr) str++; - return *str; -} - - /* Test if the byte is DBC 1st byte */ static int dbc_1st (BYTE c) { @@ -571,18 +558,14 @@ static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on uc = (BYTE)*p++; /* Get an encoding unit */ if (uc & 0x80) { /* Multiple byte code? */ - if ((uc & 0xE0) == 0xC0) { /* 2-byte sequence? */ + if ((uc & 0xE0) == 0xC0) { /* 2-byte sequence? */ uc &= 0x1F; nf = 1; - } else { - if ((uc & 0xF0) == 0xE0) { /* 3-byte sequence? */ - uc &= 0x0F; nf = 2; - } else { - if ((uc & 0xF8) == 0xF0) { /* 4-byte sequence? */ - uc &= 0x07; nf = 3; - } else { /* Wrong sequence */ - return 0xFFFFFFFF; - } - } + } else if ((uc & 0xF0) == 0xE0) { /* 3-byte sequence? */ + uc &= 0x0F; nf = 2; + } else if ((uc & 0xF8) == 0xF0) { /* 4-byte sequence? */ + uc &= 0x07; nf = 3; + } else { /* Wrong sequence */ + return 0xFFFFFFFF; } do { /* Get trailing bytes */ b = (BYTE)*p++; @@ -598,8 +581,8 @@ static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on } -/* Output a TCHAR string in defined API encoding */ -static BYTE put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */ +/* Store a Unicode char in defined API encoding */ +static UINT put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */ DWORD chr, /* UTF-16 encoded character (Surrogate pair if >=0x10000) */ TCHAR* buf, /* Output buffer */ UINT szb /* Size of the buffer */ @@ -709,14 +692,13 @@ static FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */ if (res == FR_OK) { if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { /* FAT32: Update FSInfo sector if needed */ /* Create FSInfo structure */ - mem_set(fs->win, 0, sizeof fs->win); - st_word(fs->win + BS_55AA, 0xAA55); - st_dword(fs->win + FSI_LeadSig, 0x41615252); - st_dword(fs->win + FSI_StrucSig, 0x61417272); - st_dword(fs->win + FSI_Free_Count, fs->free_clst); - st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); - /* Write it into the FSInfo sector */ - fs->winsect = fs->volbase + 1; + memset(fs->win, 0, sizeof fs->win); + st_word(fs->win + BS_55AA, 0xAA55); /* Boot signature */ + st_dword(fs->win + FSI_LeadSig, 0x41615252); /* Leading signature */ + st_dword(fs->win + FSI_StrucSig, 0x61417272); /* Structure signature */ + st_dword(fs->win + FSI_Free_Count, fs->free_clst); /* Number of free clusters */ + st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); /* Last allocated culuster */ + fs->winsect = fs->volbase + 1; /* Write it into the FSInfo sector (Next to VBR) */ disk_write(fs->pdrv, fs->win, fs->winsect, 1); fs->fsi_flag = 0; } @@ -748,7 +730,7 @@ static LBA_t clst2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */ /*-----------------------------------------------------------------------*/ -/* FAT access - Read value of a FAT entry */ +/* FAT access - Read value of an FAT entry */ /*-----------------------------------------------------------------------*/ static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */ @@ -798,7 +780,7 @@ static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFF /*-----------------------------------------------------------------------*/ -/* FAT access - Change value of a FAT entry */ +/* FAT access - Change value of an FAT entry */ /*-----------------------------------------------------------------------*/ static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ @@ -814,12 +796,12 @@ static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */ switch (fs->fs_type) { - case FS_FAT12 : + case FS_FAT12: bc = (UINT)clst; bc += bc / 2; /* bc: byte offset of the entry */ res = move_window(fs, fs->fatbase + (bc / SS(fs))); if (res != FR_OK) break; p = fs->win + bc++ % SS(fs); - *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; /* Update 1st byte */ + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; /* Update 1st byte */ fs->wflag = 1; res = move_window(fs, fs->fatbase + (bc / SS(fs))); if (res != FR_OK) break; @@ -828,14 +810,14 @@ static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ fs->wflag = 1; break; - case FS_FAT16 : + case FS_FAT16: res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); if (res != FR_OK) break; st_word(fs->win + clst * 2 % SS(fs), (WORD)val); /* Simple WORD array */ fs->wflag = 1; break; - case FS_FAT32 : + case FS_FAT32: res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); if (res != FR_OK) break; if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { @@ -997,7 +979,7 @@ static FRESULT dir_clear ( /* Returns FR_OK or FR_DISK_ERR */ if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush disk access window */ sect = clst2sect(fs, clst); /* Top of the cluster */ fs->winsect = sect; /* Set window to top of the cluster */ - mem_set(fs->win, 0, sizeof fs->win); /* Clear window buffer */ + memset(fs->win, 0, sizeof fs->win); /* Clear window buffer */ { ibuf = fs->win; szb = 1; /* Use window buffer (many single-sector writes may take a time) */ for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */ @@ -1117,7 +1099,7 @@ static FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DEN static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ DIR* dp, /* Pointer to the directory object */ - UINT nent /* Number of contiguous entries to allocate */ + UINT n_ent /* Number of contiguous entries to allocate */ ) { FRESULT res = 0; @@ -1131,13 +1113,13 @@ static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ do { res = move_window(fs, dp->sect); if (res != FR_OK) break; - if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { - if (++n == nent) break; /* A block of contiguous free entries is found */ + if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { /* Is the entry free? */ + if (++n == n_ent) break; /* Is a block of contiguous free entries found? */ } else { - n = 0; /* Not a blank entry. Restart to search */ + n = 0; /* Not a free entry, restart to search */ } - res = dir_next(dp, 1); - } while (res == FR_OK); /* Next entry with table stretch enabled */ + res = dir_next(dp, 1); /* Next entry with table stretch enabled */ + } while (res == FR_OK); } if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */ @@ -1278,7 +1260,7 @@ static void put_lfn ( do { if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective character */ st_word(dir + LfnOfs[s], wc); /* Put it */ - if (wc == 0) wc = 0xFFFF; /* Padding characters for following items */ + if (wc == 0) wc = 0xFFFF; /* Padding characters for following items */ } while (++s < 13); if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is the start of LFN sequence */ dir[LDIR_Ord] = ord; /* Set the LFN order */ @@ -1293,7 +1275,7 @@ static void put_lfn ( static void gen_numname ( BYTE* dst, /* Pointer to the buffer to store numbered SFN */ - const BYTE* src, /* Pointer to SFN */ + const BYTE* src, /* Pointer to SFN in directory form */ const WCHAR* lfn, /* Pointer to LFN */ UINT seq /* Sequence number */ ) @@ -1304,7 +1286,7 @@ static void gen_numname ( DWORD sreg = 0; - mem_cpy(dst, src, 11); + memcpy(dst, src, 11); /* Prepare the SFN to be modified */ if (seq > 5) { /* In case of many collisions, generate a hash number instead of sequential number */ sreg = seq; @@ -1319,24 +1301,23 @@ static void gen_numname ( seq = (UINT)sreg; } - /* itoa (hexadecimal) */ + /* Make suffix (~ + hexadecimal) */ i = 7; do { - c = (BYTE)((seq % 16) + '0'); + c = (BYTE)((seq % 16) + '0'); seq /= 16; if (c > '9') c += 7; ns[i--] = c; - seq /= 16; - } while (seq); + } while (i && seq); ns[i] = '~'; - /* Append the number to the SFN body */ - for (j = 0; j < i && dst[j] != ' '; j++) { - if (dbc_1st(dst[j])) { + /* Append the suffix to the SFN body */ + for (j = 0; j < i && dst[j] != ' '; j++) { /* Find the offset to append */ + if (dbc_1st(dst[j])) { /* To avoid DBC break up */ if (j == i - 1) break; j++; } } - do { + do { /* Append the suffix */ dst[j++] = (i < 8) ? ns[i++] : ' '; } while (j < 8); } @@ -1395,17 +1376,17 @@ static FRESULT dir_read ( if (b == DDEM || b == '.' || (int)((attr & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */ ord = 0xFF; } else { - if (attr == AM_LFN) { /* An LFN entry is found */ - if (b & LLEF) { /* Is it start of an LFN sequence? */ + if (attr == AM_LFN) { /* An LFN entry is found */ + if (b & LLEF) { /* Is it start of an LFN sequence? */ sum = dp->dir[LDIR_Chksum]; b &= (BYTE)~LLEF; ord = b; dp->blk_ofs = dp->dptr; } /* Check LFN validity and capture it */ ord = (b == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; - } else { /* An SFN entry is found */ + } else { /* An SFN entry is found */ if (ord != 0 || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */ - dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */ + dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */ } break; } @@ -1460,7 +1441,7 @@ static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ } } else { /* An SFN entry is found */ if (ord == 0 && sum == sum_sfn(dp->dir)) break; /* LFN matched? */ - if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */ + if (!(dp->fn[NSFLAG] & NS_LOSS) && !memcmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */ ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ } } @@ -1483,15 +1464,15 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too { FRESULT res = 0; FATFS *fs = dp->obj.fs; - UINT n = 0, nlen = 0, nent = 0; + UINT n = 0, len = 0, n_ent = 0; BYTE sn[12] = {0}, sum = 0; if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */ - for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ; /* Get lfn length */ + for (len = 0; fs->lfnbuf[len]; len++) ; /* Get lfn length */ /* On the FAT/FAT32 volume */ - mem_cpy(sn, dp->fn, 12); + memcpy(sn, dp->fn, 12); if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */ for (n = 1; n < 100; n++) { @@ -1505,19 +1486,19 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too } /* Create an SFN with/without LFNs. */ - nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1; /* Number of entries to allocate */ - res = dir_alloc(dp, nent); /* Allocate entries */ - if (res == FR_OK && --nent) { /* Set LFN entry if needed */ - res = dir_sdi(dp, dp->dptr - nent * SZDIRE); + n_ent = (sn[NSFLAG] & NS_LFN) ? (len + 12) / 13 + 1 : 1; /* Number of entries to allocate */ + res = dir_alloc(dp, n_ent); /* Allocate entries */ + if (res == FR_OK && --n_ent) { /* Set LFN entry if needed */ + res = dir_sdi(dp, dp->dptr - n_ent * SZDIRE); if (res == FR_OK) { sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */ do { /* Store LFN entries in bottom first */ res = move_window(fs, dp->sect); if (res != FR_OK) break; - put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum); + put_lfn(fs->lfnbuf, dp->dir, (BYTE)n_ent, sum); fs->wflag = 1; res = dir_next(dp, 0); /* Next entry */ - } while (res == FR_OK && --nent); + } while (res == FR_OK && --n_ent); } } @@ -1526,8 +1507,8 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too if (res == FR_OK) { res = move_window(fs, dp->sect); if (res == FR_OK) { - mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */ - mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ + memset(dp->dir, 0, SZDIRE); /* Clean the entry */ + memcpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */ fs->wflag = 1; } @@ -1558,7 +1539,7 @@ static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */ if (res != FR_OK) break; if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ dp->dir[XDIR_Type] &= 0x7F; /* Clear the entry InUse flag. */ - } else { /* On the FAT/FAT32 volume */ + } else { /* On the FAT/FAT32 volume */ dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'. */ } fs->wflag = 1; @@ -1587,6 +1568,7 @@ static void get_fileinfo ( BYTE lcf = 0; WCHAR wc = 0, hs = 0; FATFS *fs = dp->obj.fs; + UINT nw = 0; fno->fname[0] = 0; /* Invaidate file info */ @@ -1594,15 +1576,18 @@ static void get_fileinfo ( { /* On the FAT/FAT32 volume */ if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */ - si = di = hs = 0; + si = di = 0; + hs = 0; while (fs->lfnbuf[si] != 0) { wc = fs->lfnbuf[si++]; /* Get an LFN character (UTF-16) */ if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ hs = wc; continue; /* Get low surrogate */ } - wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in UTF-16 or UTF-8 encoding */ - if (wc == 0) { di = 0; break; } /* Invalid char or buffer overflow? */ - di += wc; + nw = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in API encoding */ + if (nw == 0) { /* Buffer overflow or wrong char? */ + di = 0; break; + } + di += nw; hs = 0; } if (hs != 0) di = 0; /* Broken surrogate pair? */ @@ -1620,16 +1605,20 @@ static void get_fileinfo ( wc = wc << 8 | dp->dir[si++]; } wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM -> Unicode */ - if (wc == 0) { di = 0; break; } /* Wrong char in the current code page? */ - wc = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* Store it in Unicode */ - if (wc == 0) { di = 0; break; } /* Buffer overflow? */ - di += wc; + if (wc == 0) { /* Wrong char in the current code page? */ + di = 0; break; + } + nw = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* Store it in API encoding */ + if (nw == 0) { /* Buffer overflow? */ + di = 0; break; + } + di += nw; } fno->altname[di] = 0; /* Terminate the SFN (null string means SFN is invalid) */ if (fno->fname[0] == 0) { /* If LFN is invalid, altname[] needs to be copied to fname[] */ if (di == 0) { /* If LFN and SFN both are invalid, this object is inaccessible */ - fno->fname[di++] = '?'; + fno->fname[di++] = '\?'; } else { for (si = di = 0, lcf = NS_BODY; fno->altname[si]; si++, di++) { /* Copy altname[] to fname[] with case information */ wc = (WCHAR)fno->altname[si]; @@ -1643,7 +1632,7 @@ static void get_fileinfo ( } - fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */ + fno->fattrib = dp->dir[DIR_Attr] & AM_MASK; /* Attribute */ fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ fno->ftime = ld_word(dp->dir + DIR_ModTime + 0); /* Time */ fno->fdate = ld_word(dp->dir + DIR_ModTime + 2); /* Date */ @@ -1665,10 +1654,11 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr ) { BYTE b = 0, cf = 0; - WCHAR wc = 0, *lfn = NULL; + WCHAR wc = 0; + WCHAR *lfn = NULL; + const TCHAR* p = NULL; DWORD uc = 0; UINT i = 0, ni = 0, si = 0, di = 0; - const TCHAR *p = NULL; /* Create LFN into LFN working buffer */ @@ -1678,20 +1668,21 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr if (uc == 0xFFFFFFFF) return FR_INVALID_NAME; /* Invalid code or UTF decode error */ if (uc >= 0x10000) lfn[di++] = (WCHAR)(uc >> 16); /* Store high surrogate if needed */ wc = (WCHAR)uc; - if (wc < ' ' || wc == '/' || wc == '\\') break; /* Break if end of the path or a separator is found */ - if (wc < 0x80 && chk_chr("\"*:<>\?|\x7F", wc)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ + if (wc < ' ' || IsSeparator(wc)) break; /* Break if end of the path or a separator is found */ + if (wc < 0x80 && strchr("*:<>|\"\?\x7F", (int)wc)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ if (di >= FF_MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */ - lfn[di++] = wc; /* Store the Unicode character */ + lfn[di++] = wc; /* Store the Unicode character */ } - if (wc < ' ') { /* End of path? */ - cf = NS_LAST; /* Set last segment flag */ - } else { - cf = 0; /* Next segment follows */ - while (*p == '/' || *p == '\\') p++; /* Skip duplicated separators if exist */ + if (wc < ' ') { /* Stopped at end of the path? */ + cf = NS_LAST; /* Last segment */ + } else { /* Stopped at a separator */ + while (IsSeparator(*p)) p++; /* Skip duplicated separators if exist */ + cf = 0; /* Next segment may follow */ + if (IsTerminator(*p)) cf = NS_LAST; /* Ignore terminating separator */ } *path = p; /* Return pointer to the next segment */ - while (di) { /* Snip off trailing spaces and dots if exist */ + while (di) { /* Snip off trailing spaces and dots if exist */ wc = lfn[di - 1]; if (wc != ' ' && wc != '.') break; di--; @@ -1704,7 +1695,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr if (si > 0 || lfn[si] == '.') cf |= NS_LOSS | NS_LFN; /* Is there any leading space or dot? */ while (di > 0 && lfn[di - 1] != '.') di--; /* Find last dot (di<=si: no extension) */ - mem_set(dp->fn, ' ', 11); + memset(dp->fn, ' ', 11); i = b = 0; ni = 8; for (;;) { wc = lfn[si++]; /* Get an LFN character */ @@ -1725,7 +1716,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr continue; } - if (wc >= 0x80) { /* Is this a non-ASCII character? */ + if (wc >= 0x80) { /* Is this an extended character? */ cf |= NS_LFN; /* LFN entry needs to be created */ wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */ if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */ @@ -1738,7 +1729,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr } dp->fn[i++] = (BYTE)(wc >> 8); /* Put 1st byte */ } else { /* SBC */ - if (wc == 0 || chk_chr("+,;=[]", wc)) { /* Replace illegal characters for SFN if needed */ + if (wc == 0 || strchr("+,;=[]", (int)wc)) { /* Replace illegal characters for SFN */ wc = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ } else { if (IsUpper(wc)) { /* ASCII upper case? */ @@ -1786,8 +1777,8 @@ static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ { /* With heading separator */ - while (*path == '/' || *path == '\\') path++; /* Strip heading separator */ - dp->obj.sclust = 0; /* Start from root directory */ + while (IsSeparator(*path)) path++; /* Strip separators */ + dp->obj.sclust = 0; /* Start from the root directory */ } if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */ @@ -1812,9 +1803,9 @@ static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ } break; } - if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ + if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ /* Get into the sub-directory */ - if (!(dp->obj.attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */ + if (!(dp->obj.attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */ res = FR_NO_PATH; break; } { @@ -1837,13 +1828,17 @@ static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive numb const TCHAR** path /* Pointer to pointer to the path name */ ) { - const TCHAR *tp = NULL, *tt = NULL; + const TCHAR *tp = NULL; + const TCHAR *tt = NULL; TCHAR tc = 0; - int i = 0, vol = -1; + int i = 0; + int vol = -1; tt = tp = *path; if (!tp) return vol; /* Invalid path name? */ - do tc = *tt++; while ((UINT)tc >= (FF_USE_LFN ? ' ' : '!') && tc != ':'); /* Find a colon in the path */ + do { /* Find a colon in the path */ + tc = *tt++; + } while (!IsTerminator(tc) && tc != ':'); if (tc == ':') { /* DOS/Windows style volume ID? */ i = FF_VOLUMES; @@ -1877,23 +1872,37 @@ static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive numb /* Check what the sector is */ -static UINT check_fs ( /* 0:FAT VBR, 1:exFAT VBR, 2:Valid BS but not FAT, 3:Invalid BS, 4:Disk error */ +static UINT check_fs ( /* 0:FAT/FAT32 VBR, 1:exFAT VBR, 2:Not FAT and valid BS, 3:Not FAT and invalid BS, 4:Disk error */ FATFS* fs, /* Filesystem object */ LBA_t sect /* Sector to load and check if it is an FAT-VBR or not */ ) { + WORD w = 0, sign = 0; + BYTE b = 0; + + fs->wflag = 0; fs->winsect = (LBA_t)0 - 1; /* Invaidate window */ if (move_window(fs, sect) != FR_OK) return 4; /* Load the boot sector */ - - if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot signature (always here regardless of the sector size) */ - - if (FF_FS_EXFAT && !mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* Check if exFAT VBR */ - - if (fs->win[BS_JmpBoot] == 0xE9 || fs->win[BS_JmpBoot] == 0xEB || fs->win[BS_JmpBoot] == 0xE8) { /* Valid JumpBoot code? */ - if (!mem_cmp(fs->win + BS_FilSysType, "FAT", 3)) return 0; /* Is it an FAT VBR? */ - if (!mem_cmp(fs->win + BS_FilSysType32, "FAT32", 5)) return 0; /* Is it an FAT32 VBR? */ + sign = ld_word(fs->win + BS_55AA); + b = fs->win[BS_JmpBoot]; + if (b == 0xEB || b == 0xE9 || b == 0xE8) { /* Valid JumpBoot code? (short jump, near jump or near call) */ + if (sign == 0xAA55 && !memcmp(fs->win + BS_FilSysType32, "FAT32 ", 8)) { + return 0; /* It is an FAT32 VBR */ + } + /* FAT volumes formatted with early MS-DOS lack BS_55AA and BS_FilSysType, so FAT VBR needs to be identified without them. */ + w = ld_word(fs->win + BPB_BytsPerSec); + b = fs->win[BPB_SecPerClus]; + if ((w & (w - 1)) == 0 && w >= FF_MIN_SS && w <= FF_MAX_SS /* Properness of sector size (512-4096 and 2^n) */ + && b != 0 && (b & (b - 1)) == 0 /* Properness of cluster size (2^n) */ + && ld_word(fs->win + BPB_RsvdSecCnt) != 0 /* Properness of reserved sectors (MNBZ) */ + && (UINT)fs->win[BPB_NumFATs] - 1 <= 1 /* Properness of FATs (1 or 2) */ + && ld_word(fs->win + BPB_RootEntCnt) != 0 /* Properness of root dir entries (MNBZ) */ + && (ld_word(fs->win + BPB_TotSec16) >= 128 || ld_dword(fs->win + BPB_TotSec32) >= 0x10000) /* Properness of volume sectors (>=128) */ + && ld_word(fs->win + BPB_FATSz16) != 0) { /* Properness of FAT size (MNBZ) */ + return 0; /* It can be presumed an FAT VBR */ + } } - return 2; /* Valid BS but not FAT */ + return sign == 0xAA55 ? 2 : 3; /* Not an FAT VBR (valid or invalid BS) */ } @@ -1902,15 +1911,15 @@ static UINT check_fs ( /* 0:FAT VBR, 1:exFAT VBR, 2:Valid BS but not FAT, 3:Inva static UINT find_volume ( /* Returns BS status found in the hosting drive */ FATFS* fs, /* Filesystem object */ - UINT part /* Partition to fined = 0:auto, 1..:forced */ + UINT part /* Partition to fined = 0:find as SFD and partitions, >0:forced partition number */ ) { - UINT fmt = 0, i = 0; - DWORD mbr_pt[4] = {0}; + UINT fmt, i; + DWORD mbr_pt[4]; - fmt = check_fs(fs, 0); /* Load sector 0 and check if it is an FAT VBR as SFD */ - if (fmt != 2 && (fmt >= 3 || part == 0)) return fmt; /* Returns if it is a FAT VBR as auto scan, not a BS or disk error */ + fmt = check_fs(fs, 0); /* Load sector 0 and check if it is an FAT VBR as SFD format */ + if (fmt != 2 && (fmt >= 3 || part == 0)) return fmt; /* Returns if it is an FAT VBR as auto scan, not a BS or disk error */ /* Sector 0 is not an FAT VBR or forced partition number wants a partition */ @@ -1935,15 +1944,15 @@ static UINT find_volume ( /* Returns BS status found in the hosting drive */ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ const TCHAR** path, /* Pointer to pointer to the path name (drive number) */ FATFS** rfs, /* Pointer to pointer to the found filesystem object */ - BYTE mode /* !=0: Check write protection for write access */ + BYTE mode /* Desired access mode to check write protection */ ) { int vol = 0; + FATFS *fs = NULL; DSTATUS stat = 0; LBA_t bsect = 0; DWORD tsect = 0, sysect = 0, fasize = 0, nclst = 0, szbfat = 0; WORD nrsv = 0; - FATFS *fs = NULL; UINT fmt = 0; @@ -1969,11 +1978,10 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ } /* The filesystem object is not valid. */ - /* Following code attempts to mount the volume. (find a FAT volume, analyze the BPB and initialize the filesystem object) */ + /* Following code attempts to mount the volume. (find an FAT volume, analyze the BPB and initialize the filesystem object) */ - fs->fs_type = 0; /* Clear the filesystem object */ - fs->pdrv = LD2PD(vol); /* Volume hosting physical drive */ - stat = disk_initialize(fs->pdrv); /* Initialize the physical drive */ + fs->fs_type = 0; /* Invalidate the filesystem object */ + stat = disk_initialize(fs->pdrv); /* Initialize the volume hosting physical drive */ if (stat & STA_NOINIT) { /* Check if the initialization succeeded */ return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */ } @@ -1985,11 +1993,11 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ if (SS(fs) > FF_MAX_SS || SS(fs) < FF_MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR; #endif - /* Find an FAT volume on the drive */ + /* Find an FAT volume on the hosting drive */ fmt = find_volume(fs, LD2PT(vol)); if (fmt == 4) return FR_DISK_ERR; /* An error occurred in the disk I/O layer */ if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is found */ - bsect = fs->winsect; /* Volume location */ + bsect = fs->winsect; /* Volume offset in the hosting physical drive */ /* An FAT volume is found (bsect). Following code initializes the filesystem object */ @@ -2069,7 +2077,7 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ #endif /* (FF_FS_NOFSINFO & 3) != 3 */ } - fs->fs_type = (BYTE)fmt;/* FAT sub-type */ + fs->fs_type = (BYTE)fmt;/* FAT sub-type (the filesystem object gets valid) */ fs->id = ++Fsid; /* Volume mount ID */ fs->lfnbuf = LfnBuf; /* Static LFN working buffer */ return FR_OK; @@ -2083,7 +2091,7 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ /*-----------------------------------------------------------------------*/ static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ - FFOBJID* obj, /* Pointer to the FFOBJID, the 1st member in the FIL/DIR object, to check validity */ + FFOBJID* obj, /* Pointer to the FFOBJID, the 1st member in the FIL/DIR structure, to check validity */ FATFS** rfs /* Pointer to pointer to the owner filesystem object to return */ ) { @@ -2091,11 +2099,11 @@ static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ if (obj && obj->fs && obj->fs->fs_type && obj->id == obj->fs->id) { /* Test if the object is valid */ - if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */ + if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the hosting phsical drive is kept initialized */ res = FR_OK; } } - *rfs = (res == FR_OK) ? obj->fs : 0; /* Corresponding filesystem object */ + *rfs = (res == FR_OK) ? obj->fs : 0; /* Return corresponding filesystem object if it is valid */ return res; } @@ -2115,9 +2123,9 @@ static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ /*-----------------------------------------------------------------------*/ FRESULT f_mount ( - FATFS* fs, /* Pointer to the filesystem object (NULL:unmount)*/ + FATFS* fs, /* Pointer to the filesystem object to be registered (NULL:unmount)*/ const TCHAR* path, /* Logical drive number to be mounted/unmounted */ - BYTE opt /* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */ + BYTE opt /* Mount option: 0=Do not mount (delayed mount), 1=Mount immediately */ ) { FATFS *cfs = NULL; @@ -2126,21 +2134,23 @@ FRESULT f_mount ( const TCHAR *rp = path; - /* Get logical drive number */ + /* Get volume ID (logical drive number) */ vol = get_ldnumber(&rp); if (vol < 0) return FR_INVALID_DRIVE; - cfs = FatFs[vol]; /* Pointer to fs object */ + cfs = FatFs[vol]; /* Pointer to the filesystem object of the volume */ - if (cfs) { - cfs->fs_type = 0; /* Clear old fs object */ + if (cfs) { /* Unregister current filesystem object if regsitered */ + FatFs[vol] = 0; + cfs->fs_type = 0; /* Invalidate the filesystem object to be unregistered */ } - if (fs) { - fs->fs_type = 0; /* Clear new fs object */ + if (fs) { /* Register new filesystem object */ + fs->pdrv = LD2PD(vol); /* Volume hosting physical drive */ + fs->fs_type = 0; /* Invalidate the new filesystem object */ + FatFs[vol] = fs; /* Register new fs object */ } - FatFs[vol] = fs; /* Register new fs object */ - if (opt == 0) return FR_OK; /* Do not mount now, it will be mounted later */ + if (opt == 0) return FR_OK; /* Do not mount now, it will be mounted in subsequent file functions */ res = mount_volume(&path, &fs, 0); /* Force mounted the volume */ LEAVE_FF(fs, res); @@ -2156,13 +2166,13 @@ FRESULT f_mount ( FRESULT f_open ( FIL* fp, /* Pointer to the blank file object */ const TCHAR* path, /* Pointer to the file name */ - BYTE mode /* Access mode and file open mode flags */ + BYTE mode /* Access mode and open mode flags */ ) { FRESULT res = 0; DIR dj = {0}; FATFS *fs = NULL; - DWORD cl = 0, bcs = 0, clst = 0; + DWORD cl = 0, bcs = 0, clst = 0, tm = 0; LBA_t sc = 0; FSIZE_t ofs = 0; DEF_NAMBUF @@ -2200,8 +2210,10 @@ FRESULT f_open ( if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate the file if overwrite mode */ { /* Set directory entry initial state */ + tm = GET_FATTIME(); /* Set created time */ + st_dword(dj.dir + DIR_CrtTime, tm); + st_dword(dj.dir + DIR_ModTime, tm); cl = ld_clust(fs, dj.dir); /* Get current cluster chain */ - st_dword(dj.dir + DIR_CrtTime, GET_FATTIME()); /* Set created time */ dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */ st_clust(fs, dj.dir, 0); /* Reset file allocation info */ st_dword(dj.dir + DIR_FileSize, 0); @@ -2218,7 +2230,7 @@ FRESULT f_open ( } } else { /* Open an existing file */ - if (res == FR_OK) { /* Is the object existing? */ + if (res == FR_OK) { /* Is the object exsiting? */ if (dj.obj.attr & AM_DIR) { /* File open against a directory */ res = FR_NO_FILE; } else { @@ -2239,13 +2251,13 @@ FRESULT f_open ( fp->obj.sclust = ld_clust(fs, dj.dir); /* Get object allocation info */ fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize); } - fp->obj.fs = fs; /* Validate the file object */ + fp->obj.fs = fs; /* Validate the file object */ fp->obj.id = fs->id; - fp->flag = mode; /* Set file access mode */ - fp->err = 0; /* Clear error flag */ - fp->sect = 0; /* Invalidate current data sector */ - fp->fptr = 0; /* Set file pointer top of the file */ - mem_set(fp->buf, 0, sizeof fp->buf); /* Clear sector buffer */ + fp->flag = mode; /* Set file access mode */ + fp->err = 0; /* Clear error flag */ + fp->sect = 0; /* Invalidate current data sector */ + fp->fptr = 0; /* Set file pointer top of the file */ + memset(fp->buf, 0, sizeof fp->buf); /* Clear sector buffer */ if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* Seek to end of file if FA_OPEN_APPEND is specified */ fp->fptr = fp->obj.objsize; /* Offset to seek */ bcs = (DWORD)fs->csize * SS(fs); /* Cluster size in byte */ @@ -2284,10 +2296,10 @@ FRESULT f_open ( /*-----------------------------------------------------------------------*/ FRESULT f_read ( - FIL* fp, /* Pointer to the file object */ - void* buff, /* Pointer to data buffer */ + FIL* fp, /* Open file to be read */ + void* buff, /* Data buffer to store the read data */ UINT btr, /* Number of bytes to read */ - UINT* br /* Pointer to number of bytes read */ + UINT* br /* Number of bytes read */ ) { FRESULT res = 0; @@ -2306,8 +2318,7 @@ FRESULT f_read ( remain = fp->obj.objsize - fp->fptr; if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ - for ( ; btr; /* Repeat until btr bytes read */ - btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) { + for ( ; btr > 0; btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) { /* Repeat until btr bytes read */ if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ if (csect == 0) { /* On the cluster boundary? */ @@ -2332,7 +2343,7 @@ FRESULT f_read ( } if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) { - mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); + memcpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); } rcnt = SS(fs) * cc; /* Number of bytes transferred */ continue; @@ -2348,7 +2359,7 @@ FRESULT f_read ( } rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes remains in the sector */ if (rcnt > btr) rcnt = btr; /* Clip it by btr if needed */ - mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ + memcpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ } LEAVE_FF(fs, FR_OK); @@ -2362,10 +2373,10 @@ FRESULT f_read ( /*-----------------------------------------------------------------------*/ FRESULT f_write ( - FIL* fp, /* Pointer to the file object */ - const void* buff, /* Pointer to the data to be written */ + FIL* fp, /* Open file to be written */ + const void* buff, /* Data to be written */ UINT btw, /* Number of bytes to write */ - UINT* bw /* Pointer to number of bytes written */ + UINT* bw /* Number of bytes written */ ) { FRESULT res = 0; @@ -2386,8 +2397,7 @@ FRESULT f_write ( btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); } - for ( ; btw; /* Repeat until all data written */ - btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize) { + for ( ; btw > 0; btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize) { /* Repeat until all data written */ if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */ if (csect == 0) { /* On the cluster boundary? */ @@ -2421,7 +2431,7 @@ FRESULT f_write ( } if (disk_write(fs->pdrv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ - mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs)); + memcpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs)); fp->flag &= (BYTE)~FA_DIRTY; } wcnt = SS(fs) * cc; /* Number of bytes transferred */ @@ -2436,7 +2446,7 @@ FRESULT f_write ( } wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes remains in the sector */ if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */ - mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + memcpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ fp->flag |= FA_DIRTY; } @@ -2453,7 +2463,7 @@ FRESULT f_write ( /*-----------------------------------------------------------------------*/ FRESULT f_sync ( - FIL* fp /* Pointer to the file object */ + FIL* fp /* Open file to be synced */ ) { FRESULT res = 0; @@ -2500,7 +2510,7 @@ FRESULT f_sync ( /*-----------------------------------------------------------------------*/ FRESULT f_close ( - FIL* fp /* Pointer to the file object to be closed */ + FIL* fp /* Open file to be closed */ ) { FRESULT res = 0; @@ -2701,7 +2711,7 @@ FRESULT f_readdir ( res = validate(&dp->obj, &fs); /* Check validity of the directory object */ if (res == FR_OK) { if (!fno) { - res = dir_sdi(dp, 0); /* Rewind the directory object */ + res = dir_sdi(dp, 0); /* Rewind the directory object */ } else { INIT_NAMBUF(fs); res = DIR_READ_FILE(dp); /* Read an item */ @@ -2788,8 +2798,12 @@ FRESULT f_getfree ( clst = 2; obj.fs = fs; do { stat = get_fat(&obj, clst); - if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } - if (stat == 1) { res = FR_INT_ERR; break; } + if (stat == 0xFFFFFFFF) { + res = FR_DISK_ERR; break; + } + if (stat == 1) { + res = FR_INT_ERR; break; + } if (stat == 0) nfree++; } while (++clst < fs->n_fatent); } else { @@ -2798,7 +2812,7 @@ FRESULT f_getfree ( sect = fs->fatbase; /* Top of the FAT */ i = 0; /* Offset in the sector */ do { /* Counts numbuer of entries with zero in the FAT */ - if (i == 0) { + if (i == 0) { /* New sector? */ res = move_window(fs, sect++); if (res != FR_OK) break; } @@ -2813,9 +2827,11 @@ FRESULT f_getfree ( } while (--clst); } } - *nclst = nfree; /* Return the free clusters */ - fs->free_clst = nfree; /* Now free_clst is valid */ - fs->fsi_flag |= 1; /* FAT32: FSInfo is to be updated */ + if (res == FR_OK) { /* Update parameters if succeeded */ + *nclst = nfree; /* Return the free clusters */ + fs->free_clst = nfree; /* Now free_clst is valid */ + fs->fsi_flag |= 1; /* FAT32: FSInfo is to be updated */ + } } } @@ -2882,9 +2898,9 @@ FRESULT f_unlink ( ) { FRESULT res = 0; + FATFS *fs = NULL; DIR dj = {0}, sdj = {0}; DWORD dclst = 0; - FATFS *fs = NULL; DEF_NAMBUF @@ -2911,7 +2927,7 @@ FRESULT f_unlink ( } if (dj.obj.attr & AM_DIR) { /* Is it a sub-directory? */ { - sdj.obj.fs = fs; /* Open the sub-directory */ + sdj.obj.fs = fs; /* Open the sub-directory */ sdj.obj.sclust = dclst; res = dir_sdi(&sdj, 0); if (res == FR_OK) { @@ -2948,10 +2964,10 @@ FRESULT f_mkdir ( ) { FRESULT res = 0; + FATFS *fs = NULL; DIR dj = {0}; FFOBJID sobj = {0}; - FATFS *fs = NULL; - DWORD dcl = 0, pcl = 0, tm = 0; + DWORD dcl = 0, pcl = 0, tm = 0; DEF_NAMBUF @@ -2976,12 +2992,12 @@ FRESULT f_mkdir ( res = dir_clear(fs, dcl); /* Clean up the new table */ if (res == FR_OK) { if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* Create dot entries (FAT only) */ - mem_set(fs->win + DIR_Name, ' ', 11); /* Create "." entry */ + memset(fs->win + DIR_Name, ' ', 11); /* Create "." entry */ fs->win[DIR_Name] = '.'; fs->win[DIR_Attr] = AM_DIR; st_dword(fs->win + DIR_ModTime, tm); st_clust(fs, fs->win, dcl); - mem_cpy(fs->win + SZDIRE, fs->win, SZDIRE); /* Create ".." entry */ + memcpy(fs->win + SZDIRE, fs->win, SZDIRE); /* Create ".." entry */ fs->win[SZDIRE + 1] = '.'; pcl = dj.obj.sclust; st_clust(fs, fs->win + SZDIRE, pcl); fs->wflag = 1; @@ -3022,8 +3038,8 @@ FRESULT f_rename ( ) { FRESULT res = 0; - DIR djo = {0}, djn = {0}; FATFS *fs = NULL; + DIR djo = {0}, djn = {0}; BYTE buf[(FF_FS_EXFAT) ? (SZDIRE * 2) : (SZDIRE)] = {0}, *dir = NULL; LBA_t sect = 0; DEF_NAMBUF @@ -3034,12 +3050,12 @@ FRESULT f_rename ( if (res == FR_OK) { djo.obj.fs = fs; INIT_NAMBUF(fs); - res = follow_path(&djo, path_old); /* Check old object */ + res = follow_path(&djo, path_old); /* Check old object */ if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check validity of name */ - if (res == FR_OK) { /* Object to be renamed is found */ + if (res == FR_OK) { /* Object to be renamed is found */ { /* At FAT/FAT32 volume */ - mem_cpy(buf, djo.dir, SZDIRE); /* Save directory entry of the object */ - mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ + memcpy(buf, djo.dir, SZDIRE); /* Save directory entry of the object */ + memcpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ if (res == FR_OK) { /* Is new name already in use by any other object? */ res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; @@ -3048,7 +3064,7 @@ FRESULT f_rename ( res = dir_register(&djn); /* Register the new entry */ if (res == FR_OK) { dir = djn.dir; /* Copy directory entry of the object except name */ - mem_cpy(dir + 13, buf + 13, SZDIRE - 13); + memcpy(dir + 13, buf + 13, SZDIRE - 13); dir[DIR_Attr] = buf[DIR_Attr]; if (!(dir[DIR_Attr] & AM_DIR)) dir[DIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */ fs->wflag = 1; @@ -3100,8 +3116,8 @@ FRESULT f_getlabel ( ) { FRESULT res = 0; - DIR dj = {0}; FATFS *fs = NULL; + DIR dj = {0}; UINT si = 0, di = 0; WCHAR wc = 0; @@ -3120,10 +3136,11 @@ FRESULT f_getlabel ( while (si < 11) { wc = dj.dir[si++]; if (dbc_1st((BYTE)wc) && si < 11) wc = wc << 8 | dj.dir[si++]; /* Is it a DBC? */ - wc = ff_oem2uni(wc, CODEPAGE); /* Convert it into Unicode */ - if (wc != 0) wc = put_utf(wc, &label[di], 4); /* Put it in Unicode */ - if (wc == 0) { di = 0; break; } - di += wc; + wc = ff_oem2uni(wc, CODEPAGE); /* Convert it into Unicode */ + if (wc == 0) { /* Invalid char in current code page? */ + di = 0; break; + } + di += put_utf(wc, &label[di], 4); /* Store it in Unicode */ } do { /* Truncate trailing spaces */ label[di] = 0; @@ -3144,10 +3161,12 @@ FRESULT f_getlabel ( if (res == FR_OK) { switch (fs->fs_type) { case FS_EXFAT: - di = BPB_VolIDEx; break; + di = BPB_VolIDEx; + break; case FS_FAT32: - di = BS_VolID32; break; + di = BS_VolID32; + break; default: di = BS_VolID; @@ -3170,12 +3189,12 @@ FRESULT f_setlabel ( ) { FRESULT res = 0; - DIR dj = {0}; FATFS *fs = NULL; + DIR dj = {0}; BYTE dirvn[22] = {0}; UINT di = 0; WCHAR wc = 0; - static const char badchr[] = "+.,;=[]/\\\"*:<>\?|\x7F"; /* [0..] for FAT, [7..] for exFAT */ + static const char badchr[18] = "+.,;=[]" "/*:<>|\\\"\?\x7F"; /* [0..16] for FAT, [7..16] for exFAT */ DWORD dc = 0; /* Get logical drive */ @@ -3183,12 +3202,12 @@ FRESULT f_setlabel ( if (res != FR_OK) LEAVE_FF(fs, res); { /* On the FAT/FAT32 volume */ - mem_set(dirvn, ' ', 11); + memset(dirvn, ' ', 11); di = 0; while ((UINT)*label >= ' ') { /* Create volume label */ dc = tchar2uni(&label); wc = (dc < 0x10000) ? ff_uni2oem(ff_wtoupper(dc), CODEPAGE) : 0; - if (wc == 0 || chk_chr(badchr + 0, (int)wc) || di >= (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */ + if (wc == 0 || strchr(&badchr[0], (int)wc) || di >= (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */ LEAVE_FF(fs, FR_INVALID_NAME); } if (wc >= 0x100) dirvn[di++] = (BYTE)(wc >> 8); @@ -3206,10 +3225,10 @@ FRESULT f_setlabel ( if (res == FR_OK) { if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { dj.dir[XDIR_NumLabel] = (BYTE)di; /* Change the volume label */ - mem_cpy(dj.dir + XDIR_Label, dirvn, 22); + memcpy(dj.dir + XDIR_Label, dirvn, 22); } else { if (di != 0) { - mem_cpy(dj.dir, dirvn, 11); /* Change the volume label */ + memcpy(dj.dir, dirvn, 11); /* Change the volume label */ } else { dj.dir[DIR_Name] = DDEM; /* Remove the volume label */ } @@ -3222,14 +3241,14 @@ FRESULT f_setlabel ( if (di != 0) { /* Create a volume label entry */ res = dir_alloc(&dj, 1); /* Allocate an entry */ if (res == FR_OK) { - mem_set(dj.dir, 0, SZDIRE); /* Clean the entry */ + memset(dj.dir, 0, SZDIRE); /* Clean the entry */ if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { dj.dir[XDIR_Type] = ET_VLABEL; /* Create volume label entry */ dj.dir[XDIR_NumLabel] = (BYTE)di; - mem_cpy(dj.dir + XDIR_Label, dirvn, 22); + memcpy(dj.dir + XDIR_Label, dirvn, 22); } else { dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */ - mem_cpy(dj.dir, dirvn, 11); + memcpy(dj.dir, dirvn, 11); } fs->wflag = 1; res = sync_fs(fs); @@ -3253,7 +3272,7 @@ FRESULT f_setlabel ( #if !FF_FS_READONLY && FF_USE_MKFS /*-----------------------------------------------------------------------*/ -/* Create an FAT/exFAT volume */ +/* Create FAT/exFAT volume (with sub-functions) */ /*-----------------------------------------------------------------------*/ #define N_SEC_TRACK 63 /* Sectors per track for determination of drive CHS */ @@ -3261,51 +3280,52 @@ FRESULT f_setlabel ( #define GPT_ITEMS 128 /* Number of GPT table size (>=128, sector aligned) */ -/* Create partitions on the physical drive */ +/* Create partitions on the physical drive in format of MBR or GPT */ static FRESULT create_partition ( BYTE drv, /* Physical drive number */ const LBA_t plst[], /* Partition list */ - UINT sys, /* System ID (for only MBR, temp setting) and bit8:GPT */ - BYTE* buf /* Working buffer for a sector */ + BYTE sys, /* System ID for each partition (for only MBR) */ + BYTE *buf /* Working buffer for a sector */ ) { UINT i = 0, cy = 0; LBA_t sz_drv = 0; - DWORD sz_drv32 = 0, s_lba32 = 0, n_lba32 = 0; - BYTE *pte = NULL, hd = 0, n_hd = 0, sc = 0, n_sc = 0; + DWORD sz_drv32 = 0, nxt_alloc32 = 0, sz_part32 = 0; + BYTE *pte = NULL; + BYTE hd = 0, n_hd = 0, sc = 0, n_sc = 0; - /* Get drive size */ + /* Get physical drive size */ if (disk_ioctl(drv, GET_SECTOR_COUNT, &sz_drv) != RES_OK) return FR_DISK_ERR; - { /* Create partitions in MBR */ + { /* Create partitions in MBR format */ sz_drv32 = (DWORD)sz_drv; - n_sc = N_SEC_TRACK; /* Determine drive CHS without any consideration of the drive geometry */ + n_sc = N_SEC_TRACK; /* Determine drive CHS without any consideration of the drive geometry */ for (n_hd = 8; n_hd != 0 && sz_drv32 / n_hd / n_sc > 1024; n_hd *= 2) ; - if (n_hd == 0) n_hd = 255; /* Number of heads needs to be <256 */ + if (n_hd == 0) n_hd = 255; /* Number of heads needs to be <256 */ - mem_set(buf, 0, FF_MAX_SS); /* Clear MBR */ + memset(buf, 0, FF_MAX_SS); /* Clear MBR */ pte = buf + MBR_Table; /* Partition table in the MBR */ - for (i = 0, s_lba32 = n_sc; i < 4 && s_lba32 != 0 && s_lba32 < sz_drv32; i++, s_lba32 += n_lba32) { - n_lba32 = (DWORD)plst[i]; /* Get partition size */ - if (n_lba32 <= 100) n_lba32 = (n_lba32 == 100) ? sz_drv32 : sz_drv32 / 100 * n_lba32; /* Size in percentage? */ - if (s_lba32 + n_lba32 > sz_drv32 || s_lba32 + n_lba32 < s_lba32) n_lba32 = sz_drv32 - s_lba32; /* Clip at drive size */ - if (n_lba32 == 0) break; /* End of table or no sector to allocate? */ + for (i = 0, nxt_alloc32 = n_sc; i < 4 && nxt_alloc32 != 0 && nxt_alloc32 < sz_drv32; i++, nxt_alloc32 += sz_part32) { + sz_part32 = (DWORD)plst[i]; /* Get partition size */ + if (sz_part32 <= 100) sz_part32 = (sz_part32 == 100) ? sz_drv32 : sz_drv32 / 100 * sz_part32; /* Size in percentage? */ + if (nxt_alloc32 + sz_part32 > sz_drv32 || nxt_alloc32 + sz_part32 < nxt_alloc32) sz_part32 = sz_drv32 - nxt_alloc32; /* Clip at drive size */ + if (sz_part32 == 0) break; /* End of table or no sector to allocate? */ - st_dword(pte + PTE_StLba, s_lba32); /* Start LBA */ - st_dword(pte + PTE_SizLba, n_lba32); /* Number of sectors */ - pte[PTE_System] = (BYTE)sys; /* System type */ + st_dword(pte + PTE_StLba, nxt_alloc32); /* Start LBA */ + st_dword(pte + PTE_SizLba, sz_part32); /* Number of sectors */ + pte[PTE_System] = sys; /* System type */ - cy = (UINT)(s_lba32 / n_sc / n_hd); /* Start cylinder */ - hd = (BYTE)(s_lba32 / n_sc % n_hd); /* Start head */ - sc = (BYTE)(s_lba32 % n_sc + 1); /* Start sector */ + cy = (UINT)(nxt_alloc32 / n_sc / n_hd); /* Start cylinder */ + hd = (BYTE)(nxt_alloc32 / n_sc % n_hd); /* Start head */ + sc = (BYTE)(nxt_alloc32 % n_sc + 1); /* Start sector */ pte[PTE_StHead] = hd; pte[PTE_StSec] = (BYTE)((cy >> 2 & 0xC0) | sc); pte[PTE_StCyl] = (BYTE)cy; - cy = (UINT)((s_lba32 + n_lba32 - 1) / n_sc / n_hd); /* End cylinder */ - hd = (BYTE)((s_lba32 + n_lba32 - 1) / n_sc % n_hd); /* End head */ - sc = (BYTE)((s_lba32 + n_lba32 - 1) % n_sc + 1); /* End sector */ + cy = (UINT)((nxt_alloc32 + sz_part32 - 1) / n_sc / n_hd); /* End cylinder */ + hd = (BYTE)((nxt_alloc32 + sz_part32 - 1) / n_sc % n_hd); /* End head */ + sc = (BYTE)((nxt_alloc32 + sz_part32 - 1) % n_sc + 1); /* End sector */ pte[PTE_EdHead] = hd; pte[PTE_EdSec] = (BYTE)((cy >> 2 & 0xC0) | sc); pte[PTE_EdCyl] = (BYTE)cy; @@ -3325,40 +3345,44 @@ static FRESULT create_partition ( FRESULT f_mkfs ( const TCHAR* path, /* Logical drive number */ const MKFS_PARM* opt, /* Format options */ - void* work, /* Pointer to working buffer (null: use heap memory) */ + void* work, /* Pointer to working buffer (null: use len bytes of heap memory) */ UINT len /* Size of working buffer [byte] */ ) { static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT volume (4Ks unit) */ static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */ static const MKFS_PARM defopt = {FM_ANY, 0, 0, 0, 0}; /* Default parameter */ - BYTE fsopt = 0, fsty = 0, sys = 0, *buf = NULL, *pte = NULL, pdrv = 0, ipart = 0; - WORD ss; /* Sector size */ - DWORD sz_buf = 0, sz_blk = 0, n_clst = 0, pau = 0, nsect = 0, n = 0; + BYTE fsopt = 0, fsty = 0, sys = 0, pdrv = 0, ipart = 0; + BYTE *buf = NULL; + BYTE *pte = NULL; + WORD ss = 0; /* Sector size */ + DWORD sz_buf = 0, sz_blk = 0, n_clst = 0, pau = 0, nsect = 0, n = 0, vsn = 0; LBA_t sz_vol, b_vol, b_fat, b_data; /* Size of volume, Base LBA of volume, fat, data */ LBA_t sect = 0, lba[2] = {0}; DWORD sz_rsv, sz_fat, sz_dir, sz_au; /* Size of reserved, fat, dir, data, cluster */ UINT n_fat, n_root, i; /* Index, Number of FATs and Number of roor dir entries */ int vol = 0; DSTATUS ds = 0; - FRESULT fr = 0; + FRESULT res = 0; /* Check mounted drive and clear work area */ vol = get_ldnumber(&path); /* Get target logical drive */ if (vol < 0) return FR_INVALID_DRIVE; if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear the fs object if mounted */ - pdrv = LD2PD(vol); /* Physical drive */ - ipart = LD2PT(vol); /* Partition (0:create as new, 1..:get from partition table) */ - if (!opt) opt = &defopt; /* Use default parameter if it is not given */ + pdrv = LD2PD(vol); /* Hosting physical drive */ + ipart = LD2PT(vol); /* Hosting partition (0:create as new, 1..:existing partition) */ - /* Get physical drive status (sz_drv, sz_blk, ss) */ + /* Initialize the hosting physical drive */ ds = disk_initialize(pdrv); if (ds & STA_NOINIT) return FR_NOT_READY; if (ds & STA_PROTECT) return FR_WRITE_PROTECTED; + + /* Get physical drive parameters (sz_drv, sz_blk and ss) */ + if (!opt) opt = &defopt; /* Use default parameter if it is not given */ sz_blk = opt->align; - if (sz_blk == 0 && disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK) sz_blk = 1; - if (sz_blk == 0 || sz_blk > 0x8000 || (sz_blk & (sz_blk - 1))) sz_blk = 1; + if (sz_blk == 0) disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk); /* Block size from the paramter or lower layer */ + if (sz_blk == 0 || sz_blk > 0x8000 || (sz_blk & (sz_blk - 1))) sz_blk = 1; /* Use default if the block size is invalid */ #if FF_MAX_SS != FF_MIN_SS if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR; if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR; @@ -3403,7 +3427,7 @@ FRESULT f_mkfs ( } if (sz_vol < 128) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if volume size is >=128s */ - /* Now start to create a FAT volume at b_vol and sz_vol */ + /* Now start to create an FAT volume at b_vol and sz_vol */ do { /* Pre-determine the FAT type */ if (FF_FS_EXFAT && (fsopt & FM_EXFAT)) { /* exFAT possible? */ @@ -3421,6 +3445,7 @@ FRESULT f_mkfs ( fsty = FS_FAT16; } while (0); + vsn = (DWORD)sz_vol + GET_FATTIME(); /* VSN generated from current time and partitiion size */ { /* Create an FAT/FAT32 volume */ do { pau = sz_au; @@ -3477,7 +3502,7 @@ FRESULT f_mkfs ( if (fsty == FS_FAT16) { if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */ if (sz_au == 0 && (pau * 2) <= 64) { - sz_au = pau * 2; continue; /* Adjust cluster size and retry */ + sz_au = pau * 2; continue; /* Adjust cluster size and retry */ } if ((fsopt & FM_FAT32)) { fsty = FS_FAT32; continue; /* Switch type to FAT32 and retry */ @@ -3497,8 +3522,8 @@ FRESULT f_mkfs ( } while (1); /* Create FAT VBR */ - mem_set(buf, 0, ss); - mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code (x86), OEM name */ + memset(buf, 0, ss); + memcpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11); /* Boot jump code (x86), OEM name */ st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */ buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */ st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */ @@ -3514,20 +3539,20 @@ FRESULT f_mkfs ( st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */ st_dword(buf + BPB_HiddSec, (DWORD)b_vol); /* Volume offset in the physical drive [sector] */ if (fsty == FS_FAT32) { - st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */ + st_dword(buf + BS_VolID32, vsn); /* VSN */ st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */ st_dword(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */ st_word(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */ st_word(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */ buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */ buf[BS_BootSig32] = 0x29; /* Extended boot signature */ - mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ + memcpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ } else { - st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */ + st_dword(buf + BS_VolID, vsn); /* VSN */ st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */ buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */ buf[BS_BootSig] = 0x29; /* Extended boot signature */ - mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ + memcpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ } st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */ if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the VBR sector */ @@ -3535,7 +3560,7 @@ FRESULT f_mkfs ( /* Create FSINFO record if needed */ if (fsty == FS_FAT32) { disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */ - mem_set(buf, 0, ss); + memset(buf, 0, ss); st_dword(buf + FSI_LeadSig, 0x41615252); st_dword(buf + FSI_StrucSig, 0x61417272); st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ @@ -3546,7 +3571,7 @@ FRESULT f_mkfs ( } /* Initialize FAT area */ - mem_set(buf, 0, sz_buf * ss); + memset(buf, 0, sz_buf * ss); sect = b_fat; /* FAT start sector */ for (i = 0; i < n_fat; i++) { /* Initialize FATs each */ if (fsty == FS_FAT32) { @@ -3560,7 +3585,7 @@ FRESULT f_mkfs ( do { /* Fill FAT sectors */ n = (nsect > sz_buf) ? sz_buf : nsect; if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); - mem_set(buf, 0, ss); /* Rest of FAT all are cleared */ + memset(buf, 0, ss); /* Rest of FAT all are cleared */ sect += n; nsect -= n; } while (nsect); } @@ -3578,32 +3603,30 @@ FRESULT f_mkfs ( /* Determine system ID in the MBR partition table */ if (FF_FS_EXFAT && fsty == FS_EXFAT) { - sys = 0x07; /* exFAT */ + sys = 0x07; /* exFAT */ + } else if (fsty == FS_FAT32) { + sys = 0x0C; /* FAT32X */ + } else if (sz_vol >= 0x10000) { + sys = 0x06; /* FAT12/16 (large) */ + } else if (fsty == FS_FAT16) { + sys = 0x04; /* FAT16 */ } else { - if (fsty == FS_FAT32) { - sys = 0x0C; /* FAT32X */ - } else { - if (sz_vol >= 0x10000) { - sys = 0x06; /* FAT12/16 (large) */ - } else { - sys = (fsty == FS_FAT16) ? 0x04 : 0x01; /* FAT16 : FAT12 */ - } - } + sys = 0x01; /* FAT12 */ } /* Update partition information */ if (FF_MULTI_PARTITION && ipart != 0) { /* Volume is in the existing partition */ - if (!FF_LBA64 || !(fsopt & 0x80)) { + if (!FF_LBA64 || !(fsopt & 0x80)) { /* Is the partition in MBR? */ /* Update system ID in the partition table */ if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Read the MBR */ buf[MBR_Table + (ipart - 1) * SZ_PTE + PTE_System] = sys; /* Set system ID */ if (disk_write(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it back to the MBR */ } } else { /* Volume as a new single partition */ - if (!(fsopt & FM_SFD)) { /* Create partition table if not in SFD */ - lba[0] = sz_vol, lba[1] = 0; - fr = create_partition(pdrv, lba, sys, buf); - if (fr != FR_OK) LEAVE_MKFS(fr); + if (!(fsopt & FM_SFD)) { /* Create partition table if not in SFD format */ + lba[0] = sz_vol; lba[1] = 0; + res = create_partition(pdrv, lba, sys, buf); + if (res != FR_OK) LEAVE_MKFS(res); } } diff --git a/core/embed/extmod/modtrezorio/ff.h b/core/embed/extmod/modtrezorio/ff.h index 434282c150..88fb318750 100644 --- a/core/embed/extmod/modtrezorio/ff.h +++ b/core/embed/extmod/modtrezorio/ff.h @@ -1,10 +1,10 @@ // clang-format off /*----------------------------------------------------------------------------/ -/ FatFs - Generic FAT Filesystem module R0.14 / +/ FatFs - Generic FAT Filesystem module R0.15 / /-----------------------------------------------------------------------------/ / -/ Copyright (C) 2019, ChaN, all right reserved. +/ Copyright (C) 2022, ChaN, all right reserved. / / FatFs module is an open source software. Redistribution and use of FatFs in / source and binary forms, with or without modification, are permitted provided @@ -22,7 +22,7 @@ #ifndef FF_DEFINED -#define FF_DEFINED 86606 /* Revision ID */ +#define FF_DEFINED 80286 /* Revision ID */ #ifdef __cplusplus extern "C" { @@ -37,10 +37,14 @@ extern "C" { /* Integer types used for FatFs API */ -#if defined(_WIN32) /* Main development platform */ +#if defined(_WIN32) /* Windows VC++ (for development only) */ #define FF_INTDEF 2 #include typedef unsigned __int64 QWORD; +#include +#define isnan(v) _isnan(v) +#define isinf(v) (!_finite(v)) + #elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */ #define FF_INTDEF 2 #include @@ -50,6 +54,7 @@ typedef uint16_t WORD; /* 16-bit unsigned integer */ typedef uint32_t DWORD; /* 32-bit unsigned integer */ typedef uint64_t QWORD; /* 64-bit unsigned integer */ typedef WORD WCHAR; /* UTF-16 character type */ + #else /* Earlier than C99 */ #define FF_INTDEF 1 typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ @@ -60,53 +65,6 @@ typedef WORD WCHAR; /* UTF-16 character type */ #endif -/* Definitions of volume management */ - -#if FF_MULTI_PARTITION /* Multiple partition configuration */ -typedef struct { - BYTE pd; /* Physical drive number */ - BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ -} PARTITION; -extern PARTITION VolToPart[]; /* Volume - Partition mapping table */ -#endif - -#if FF_STR_VOLUME_ID -#ifndef FF_VOLUME_STRS -extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */ -#endif -#endif - - - -/* Type of path name strings on FatFs API */ - -#ifndef _INC_TCHAR -#define _INC_TCHAR - -#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */ -typedef WCHAR TCHAR; -#define _T(x) L ## x -#define _TEXT(x) L ## x -#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */ -typedef char TCHAR; -#define _T(x) u8 ## x -#define _TEXT(x) u8 ## x -#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */ -typedef DWORD TCHAR; -#define _T(x) U ## x -#define _TEXT(x) U ## x -#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3) -#error Wrong FF_LFN_UNICODE setting -#else /* ANSI/OEM code in SBCS/DBCS */ -typedef char TCHAR; -#define _T(x) x -#define _TEXT(x) x -#endif - -#endif - - - /* Type of file size and LBA variables */ #if FF_FS_EXFAT @@ -129,14 +87,57 @@ typedef DWORD LBA_t; +/* Type of path name strings on FatFs API (TCHAR) */ + +#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */ +typedef WCHAR TCHAR; +#define _T(x) L ## x +#define _TEXT(x) L ## x +#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */ +typedef char TCHAR; +#define _T(x) u8 ## x +#define _TEXT(x) u8 ## x +#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */ +typedef DWORD TCHAR; +#define _T(x) U ## x +#define _TEXT(x) U ## x +#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3) +#error Wrong FF_LFN_UNICODE setting +#else /* ANSI/OEM code in SBCS/DBCS */ +typedef char TCHAR; +#define _T(x) x +#define _TEXT(x) x +#endif + + + +/* Definitions of volume management */ + +#if FF_MULTI_PARTITION /* Multiple partition configuration */ +typedef struct { + BYTE pd; /* Physical drive number */ + BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ +} PARTITION; +extern PARTITION VolToPart[]; /* Volume - Partition mapping table */ +#endif + +#if FF_STR_VOLUME_ID +#ifndef FF_VOLUME_STRS +extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */ +#endif +#endif + + + /* Filesystem object structure (FATFS) */ typedef struct { BYTE fs_type; /* Filesystem type (0:not mounted) */ - BYTE pdrv; /* Associated physical drive */ + BYTE pdrv; /* Volume hosting physical drive */ + BYTE ldrv; /* Logical drive number (used only when FF_FS_REENTRANT) */ BYTE n_fats; /* Number of FATs (1 or 2) */ - BYTE wflag; /* win[] flag (b0:dirty) */ - BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ + BYTE wflag; /* win[] status (b0:dirty) */ + BYTE fsi_flag; /* FSINFO status (b7:disabled, b0:dirty) */ WORD id; /* Volume mount ID */ WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ WORD csize; /* Cluster size [sectors] */ @@ -149,9 +150,6 @@ typedef struct { #if FF_FS_EXFAT BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */ #endif -#if FF_FS_REENTRANT - FF_SYNC_t sobj; /* Identifier of sync object */ -#endif #if !FF_FS_READONLY DWORD last_clst; /* Last allocated cluster */ DWORD free_clst; /* Number of free clusters */ @@ -165,10 +163,10 @@ typedef struct { #endif #endif DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */ - DWORD fsize; /* Size of an FAT [sectors] */ + DWORD fsize; /* Number of sectors per FAT */ LBA_t volbase; /* Volume base sector */ LBA_t fatbase; /* FAT base sector */ - LBA_t dirbase; /* Root directory base sector/cluster */ + LBA_t dirbase; /* Root directory base sector (FAT12/16) or cluster (FAT32/exFAT) */ LBA_t database; /* Data base sector */ #if FF_FS_EXFAT LBA_t bitbase; /* Allocation bitmap base sector */ @@ -183,7 +181,7 @@ typedef struct { typedef struct { FATFS* fs; /* Pointer to the hosting volume of this object */ - WORD id; /* Hosting volume mount ID */ + WORD id; /* Hosting volume's mount ID */ BYTE attr; /* Object attribute */ BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */ DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */ @@ -300,8 +298,10 @@ typedef enum { + +/*--------------------------------------------------------------*/ +/* FatFs Module Application Interface */ /*--------------------------------------------------------------*/ -/* FatFs module application interface */ FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ FRESULT f_close (FIL* fp); /* Close an open file object */ @@ -338,6 +338,8 @@ int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */ int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */ +/* Some API fucntions are implemented as macro */ + #define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) #define f_error(fp) ((fp)->err) #define f_tell(fp) ((fp)->fptr) @@ -347,46 +349,47 @@ TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the fil #define f_rmdir(path) f_unlink(path) #define f_unmount(path) f_mount(0, path, 0) -#ifndef EOF -#define EOF (-1) -#endif - /*--------------------------------------------------------------*/ -/* Additional user defined functions */ +/* Additional Functions */ +/*--------------------------------------------------------------*/ -/* RTC function */ +/* RTC function (provided by user) */ #if !FF_FS_READONLY && !FF_FS_NORTC -DWORD get_fattime (void); +DWORD get_fattime (void); /* Get current time */ #endif -/* LFN support functions */ -#if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */ + +/* LFN support functions (defined in ffunicode.c) */ + +#if FF_USE_LFN >= 1 WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */ WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */ DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */ #endif -#if FF_USE_LFN == 3 /* Dynamic memory allocation */ -void* ff_memalloc (UINT msize); /* Allocate memory block */ -void ff_memfree (void* mblock); /* Free memory block */ -#endif -/* Sync functions */ -#if FF_FS_REENTRANT -int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */ -int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */ -void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */ -int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */ + +/* O/S dependent functions (samples available in ffsystem.c) */ + +#if FF_USE_LFN == 3 /* Dynamic memory allocation */ +void* ff_memalloc (UINT msize); /* Allocate memory block */ +void ff_memfree (void* mblock); /* Free memory block */ +#endif +#if FF_FS_REENTRANT /* Sync functions */ +int ff_mutex_create (int vol); /* Create a sync object */ +void ff_mutex_delete (int vol); /* Delete a sync object */ +int ff_mutex_take (int vol); /* Lock sync object */ +void ff_mutex_give (int vol); /* Unlock sync object */ #endif /*--------------------------------------------------------------*/ -/* Flags and offset address */ - +/* Flags and Offset Address */ +/*--------------------------------------------------------------*/ /* File access mode and open method flags (3rd argument of f_open) */ #define FA_READ 0x01 diff --git a/core/embed/extmod/modtrezorio/ffconf.h b/core/embed/extmod/modtrezorio/ffconf.h index 8e127d2c9e..63bd625a1f 100644 --- a/core/embed/extmod/modtrezorio/ffconf.h +++ b/core/embed/extmod/modtrezorio/ffconf.h @@ -1,10 +1,10 @@ // clang-format off /*---------------------------------------------------------------------------/ -/ FatFs Functional Configurations +/ Configurations of FatFs Module /---------------------------------------------------------------------------*/ -#define FFCONF_DEF 86606 /* Revision ID */ +#define FFCONF_DEF 80286 /* Revision ID */ /*---------------------------------------------------------------------------/ / Function Configurations @@ -27,14 +27,6 @@ / 3: f_lseek() function is removed in addition to 2. */ -#define FF_USE_STRFUNC 0 -/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf(). -/ -/ 0: Disable string functions. -/ 1: Enable without LF-CRLF conversion. -/ 2: Enable with LF-CRLF conversion. */ - - #define FF_USE_FIND 0 /* This option switches filtered directory read functions, f_findfirst() and / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ @@ -66,6 +58,30 @@ /* This option switches f_forward() function. (0:Disable or 1:Enable) */ +#define FF_USE_STRFUNC 0 +#define FF_PRINT_LLI 1 +#define FF_PRINT_FLOAT 1 +#define FF_STRF_ENCODE 3 +/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and +/ f_printf(). +/ +/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect. +/ 1: Enable without LF-CRLF conversion. +/ 2: Enable with LF-CRLF conversion. +/ +/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2 +/ makes f_printf() support floating point argument. These features want C99 or later. +/ When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character +/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE +/ to be read/written via those functions. +/ +/ 0: ANSI/OEM in current CP +/ 1: Unicode in UTF-16LE +/ 2: Unicode in UTF-16BE +/ 3: Unicode in UTF-8 +*/ + + /*---------------------------------------------------------------------------/ / Locale and Namespace Configurations /---------------------------------------------------------------------------*/ @@ -139,19 +155,6 @@ / on character encoding. When LFN is not enabled, these options have no effect. */ -#define FF_STRF_ENCODE 3 -/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(), -/ f_putc(), f_puts and f_printf() convert the character encoding in it. -/ This option selects assumption of character encoding ON THE FILE to be -/ read/written via those functions. -/ -/ 0: ANSI/OEM in current CP -/ 1: Unicode in UTF-16LE -/ 2: Unicode in UTF-16BE -/ 3: Unicode in UTF-8 -*/ - - #define FF_FS_RPATH 0 /* This option configures support for relative path. / @@ -177,7 +180,7 @@ / logical drives. Number of items must not be less than FF_VOLUMES. Valid / characters for the volume ID strings are A-Z, a-z and 0-9, however, they are / compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is -/ not defined, a user defined volume string table needs to be defined as: +/ not defined, a user defined volume string table is needed as: / / const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",... */ @@ -196,7 +199,7 @@ #define FF_MAX_SS 512 /* This set of options configures the range of sector size to be supported. (512, / 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and -/ harddisk. But a larger value may be required for on-board flash memory and some +/ harddisk, but a larger value may be required for on-board flash memory and some / type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured / for variable sector size mode and disk_ioctl() function needs to implement / GET_SECTOR_SIZE command. */ @@ -207,8 +210,8 @@ / To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */ -#define FF_MIN_GPT 0x100000000 -/* Minimum number of sectors to switch GPT format to create partition in f_mkfs and +#define FF_MIN_GPT 0x10000000 +/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and / f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */ @@ -240,9 +243,9 @@ #define FF_NORTC_MON 1 #define FF_NORTC_MDAY 1 #define FF_NORTC_YEAR 2019 -/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have -/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable -/ the timestamp function. Every object modified by FatFs will have a fixed timestamp +/* The option FF_FS_NORTC switches timestamp feature. If the system does not have +/ an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the +/ timestamp feature. Every object modified by FatFs will have a fixed timestamp / defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time. / To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be / added to the project to read current time form real-time clock. FF_NORTC_MON, @@ -252,7 +255,7 @@ #define FF_FS_NOFSINFO 0 /* If you need to know correct free space on the FAT32 volume, set bit 0 of this -/ option, and f_getfree() function at first time after volume mount will force +/ option, and f_getfree() function at the first time after volume mount will force / a full FAT scan. Bit 1 controls the use of last allocated cluster number. / / bit0=0: Use free cluster count in the FSINFO if available. @@ -274,26 +277,21 @@ / lock control is independent of re-entrancy. */ -/* #include // O/S definitions */ #define FF_FS_REENTRANT 0 #define FF_FS_TIMEOUT 1000 -#define FF_SYNC_t HANDLE /* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs / module itself. Note that regardless of this option, file access to different / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() / and f_fdisk() function, are always not re-entrant. Only file/directory access -/ to the same volume is under control of this function. +/ to the same volume is under control of this featuer. / -/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect. +/ 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect. / 1: Enable re-entrancy. Also user provided synchronization handlers, -/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() -/ function, must be added to the project. Samples are available in -/ option/syscall.c. +/ ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give() +/ function, must be added to the project. Samples are available in ffsystem.c. / -/ The FF_FS_TIMEOUT defines timeout period in unit of time tick. -/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, -/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be -/ included somewhere in the scope of ff.h. */ +/ The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick. +*/ diff --git a/core/embed/extmod/modtrezorio/ffunicode.c b/core/embed/extmod/modtrezorio/ffunicode.c index 3267a822ff..2e4bf57619 100644 --- a/core/embed/extmod/modtrezorio/ffunicode.c +++ b/core/embed/extmod/modtrezorio/ffunicode.c @@ -1,15 +1,15 @@ // clang-format off /*------------------------------------------------------------------------*/ -/* Unicode handling functions for FatFs R0.13+ */ +/* Unicode Handling Functions for FatFs R0.13 and Later */ +/*------------------------------------------------------------------------*/ +/* This module will occupy a huge memory in the .rodata section when the */ +/* FatFs is configured for LFN with DBCS. If the system has a Unicode */ +/* library for the code conversion, this module should be modified to use */ +/* it to avoid silly memory consumption. */ /*------------------------------------------------------------------------*/ -/* This module will occupy a huge memory in the .const section when the / -/ FatFs is configured for LFN with DBCS. If the system has any Unicode / -/ utilitiy for the code conversion, this module should be modified to use / -/ that function to avoid silly memory consumption. / -/-------------------------------------------------------------------------*/ /* -/ Copyright (C) 2014, ChaN, all right reserved. +/ Copyright (C) 2022, ChaN, all right reserved. / / FatFs module is an open source software. Redistribution and use of FatFs in / source and binary forms, with or without modification, are permitted provided @@ -27,7 +27,7 @@ #include "ff.h" -#if FF_USE_LFN /* This module will be blanked if non-LFN configuration */ +#if FF_USE_LFN != 0 /* This module will be blanked if in non-LFN configuration */ #define MERGE2(a, b) a ## b #define CVTBL(tbl, cp) MERGE2(tbl, cp) @@ -56,8 +56,8 @@ static const WCHAR uc437[] = { /* CP437(U.S.) to Unicode conversion table */ /*------------------------------------------------------------------------*/ -/* OEM <==> Unicode conversions for static code page configuration */ -/* SBCS fixed code page */ +/* OEM <==> Unicode Conversions for Static Code Page Configuration with */ +/* SBCS Fixed Code Page */ /*------------------------------------------------------------------------*/ WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */ @@ -66,7 +66,7 @@ WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */ ) { WCHAR c = 0; - const WCHAR *p = CVTBL(uc, FF_CODE_PAGE); + const WCHAR* p = CVTBL(uc, FF_CODE_PAGE); if (uni < 0x80) { /* ASCII? */ @@ -88,7 +88,7 @@ WCHAR ff_oem2uni ( /* Returns Unicode character in UTF-16, zero on error */ ) { WCHAR c = 0; - const WCHAR *p = CVTBL(uc, FF_CODE_PAGE); + const WCHAR* p = CVTBL(uc, FF_CODE_PAGE); if (oem < 0x80) { /* ASCII? */ @@ -105,24 +105,8 @@ WCHAR ff_oem2uni ( /* Returns Unicode character in UTF-16, zero on error */ - /*------------------------------------------------------------------------*/ -/* OEM <==> Unicode conversions for static code page configuration */ -/* DBCS fixed code page */ -/*------------------------------------------------------------------------*/ - - - - -/*------------------------------------------------------------------------*/ -/* OEM <==> Unicode conversions for dynamic code page configuration */ -/*------------------------------------------------------------------------*/ - - - - -/*------------------------------------------------------------------------*/ -/* Unicode up-case conversion */ +/* Unicode Up-case Conversion */ /*------------------------------------------------------------------------*/ DWORD ff_wtoupper ( /* Returns up-converted code point */ @@ -254,4 +238,4 @@ DWORD ff_wtoupper ( /* Returns up-converted code point */ } -#endif /* #if FF_USE_LFN */ +#endif /* #if FF_USE_LFN != 0 */ diff --git a/core/embed/extmod/modtrezorui/buffers.c b/core/embed/extmod/modtrezorui/buffers.c index b3d44d86b4..0d987a6e73 100644 --- a/core/embed/extmod/modtrezorui/buffers.c +++ b/core/embed/extmod/modtrezorui/buffers.c @@ -22,17 +22,12 @@ #include "fonts/fonts.h" #include "memzero.h" -#if USE_DMA2D - -#if defined BOOTLOADER -#define BUFFER_SECTION __attribute__((section(".buf"))) -#else -#define BUFFER_SECTION -#endif - #define BUFFERS_16BPP 3 #define BUFFERS_4BPP 3 #define BUFFERS_TEXT 1 +#define BUFFERS_JPEG 1 +#define BUFFERS_JPEG_WORK 1 +#define BUFFERS_BLURRING 1 const int32_t text_buffer_height = FONT_MAX_HEIGHT; const int32_t buffer_width = DISPLAY_RESX; @@ -40,6 +35,9 @@ const int32_t buffer_width = DISPLAY_RESX; BUFFER_SECTION line_buffer_16bpp_t line_buffers_16bpp[BUFFERS_16BPP]; BUFFER_SECTION line_buffer_4bpp_t line_buffers_4bpp[BUFFERS_4BPP]; BUFFER_SECTION buffer_text_t text_buffers[BUFFERS_TEXT]; +NODMA_BUFFER_SECTION buffer_jpeg_t jpeg_buffers[BUFFERS_JPEG]; +NODMA_BUFFER_SECTION buffer_jpeg_work_t jpeg_work_buffers[BUFFERS_JPEG_WORK]; +NODMA_BUFFER_SECTION buffer_blurring_t blurring_buffers[BUFFERS_BLURRING]; line_buffer_16bpp_t* buffers_get_line_buffer_16bpp(uint16_t idx, bool clear) { if (idx >= BUFFERS_16BPP) { @@ -71,4 +69,35 @@ buffer_text_t* buffers_get_text_buffer(uint16_t idx, bool clear) { return &text_buffers[idx]; } -#endif +buffer_jpeg_t* buffers_get_jpeg_buffer(uint16_t idx, bool clear) { + if (idx >= BUFFERS_JPEG) { + return NULL; + } + + if (clear) { + memzero(&jpeg_buffers[idx], sizeof(jpeg_buffers[idx])); + } + return &jpeg_buffers[idx]; +} + +buffer_jpeg_work_t* buffers_get_jpeg_work_buffer(uint16_t idx, bool clear) { + if (idx >= BUFFERS_JPEG_WORK) { + return NULL; + } + + if (clear) { + memzero(&jpeg_work_buffers[idx], sizeof(jpeg_work_buffers[idx])); + } + return &jpeg_work_buffers[idx]; +} + +buffer_blurring_t* buffers_get_blurring_buffer(uint16_t idx, bool clear) { + if (idx >= BUFFERS_BLURRING) { + return NULL; + } + + if (clear) { + memzero(&blurring_buffers[idx], sizeof(blurring_buffers[idx])); + } + return &blurring_buffers[idx]; +} diff --git a/core/embed/extmod/modtrezorui/buffers.h b/core/embed/extmod/modtrezorui/buffers.h index 89f284d837..339876fe73 100644 --- a/core/embed/extmod/modtrezorui/buffers.h +++ b/core/embed/extmod/modtrezorui/buffers.h @@ -27,15 +27,33 @@ #define BUFFER_PIXELS DISPLAY_RESX -#define TEXT_BUFFER_HEIGHT 24 +#define TEXT_BUFFER_HEIGHT 32 #if TEXT_BUFFER_HEIGHT < FONT_MAX_HEIGHT #error Text buffer height is too small, please adjust to match used fonts #endif -#define LINE_BUFFER_16BPP_SIZE BUFFER_PIXELS * 2 -#define LINE_BUFFER_4BPP_SIZE BUFFER_PIXELS / 2 -#define TEXT_BUFFER_SIZE (BUFFER_PIXELS * TEXT_BUFFER_HEIGHT) / 2 +#define LINE_BUFFER_16BPP_SIZE (BUFFER_PIXELS * 2) +#define LINE_BUFFER_4BPP_SIZE (BUFFER_PIXELS / 2) +#define TEXT_BUFFER_SIZE ((BUFFER_PIXELS * TEXT_BUFFER_HEIGHT) / 2) +#define JPEG_BUFFER_SIZE (BUFFER_PIXELS * 16) + +// 3100 is needed according to tjpgd docs, +// 256 because we need non overlapping memory in rust +// 6 << 10 is for huffman decoding table +#define JPEG_WORK_SIZE (3100 + 256 + (6 << 10)) + +#if defined BOOTLOADER +#define BUFFER_SECTION __attribute__((section(".buf"))) +#else +#define BUFFER_SECTION +#endif + +#if defined BOOTLOADER || defined TREZOR_EMULATOR +#define NODMA_BUFFER_SECTION +#else +#define NODMA_BUFFER_SECTION __attribute__((section(".no_dma_buffers"))) +#endif typedef __attribute__((aligned(4))) struct { uint8_t buffer[LINE_BUFFER_16BPP_SIZE]; @@ -49,11 +67,26 @@ typedef __attribute__((aligned(4))) struct { uint8_t buffer[TEXT_BUFFER_SIZE]; } buffer_text_t; +typedef __attribute__((aligned(4))) struct { + uint16_t buffer[JPEG_BUFFER_SIZE]; +} buffer_jpeg_t; + +typedef __attribute__((aligned(4))) struct { + uint8_t buffer[JPEG_WORK_SIZE]; +} buffer_jpeg_work_t; + +typedef __attribute__((aligned(4))) struct { + uint16_t buffer[10][3][BUFFER_PIXELS]; +} buffer_blurring_t; + extern const int32_t text_buffer_height; extern const int32_t buffer_width; line_buffer_16bpp_t* buffers_get_line_buffer_16bpp(uint16_t idx, bool clear); line_buffer_4bpp_t* buffers_get_line_buffer_4bpp(uint16_t idx, bool clear); buffer_text_t* buffers_get_text_buffer(uint16_t idx, bool clear); +buffer_jpeg_t* buffers_get_jpeg_buffer(uint16_t idx, bool clear); +buffer_jpeg_work_t* buffers_get_jpeg_work_buffer(uint16_t idx, bool clear); +buffer_blurring_t* buffers_get_blurring_buffer(uint16_t idx, bool clear); #endif // _BUFFERS_H diff --git a/core/embed/extmod/modtrezorui/display.c b/core/embed/extmod/modtrezorui/display.c index b91f1cffbb..b848fc5ba6 100644 --- a/core/embed/extmod/modtrezorui/display.c +++ b/core/embed/extmod/modtrezorui/display.c @@ -149,6 +149,45 @@ void display_bar_radius(int x, int y, int w, int h, uint16_t c, uint16_t b, PIXELDATA_DIRTY(); } +void display_bar_radius_buffer(int x, int y, int w, int h, uint8_t r, + buffer_text_t *buffer) { + if (h > 32) { + return; + } + if (r != 2 && r != 4 && r != 8 && r != 16) { + return; + } else { + r = 16 / r; + } + int x0 = 0, y0 = 0, x1 = 0, y1 = 0; + clamp_coords(x, y, w, h, &x0, &y0, &x1, &y1); + for (int j = y0; j <= y1; j++) { + for (int i = x0; i <= x1; i++) { + int rx = i - x; + int ry = j - y; + int p = j * DISPLAY_RESX + i; + uint8_t c = 0; + if (rx < CORNER_RADIUS / r && ry < CORNER_RADIUS / r) { + c = cornertable[rx * r + ry * r * CORNER_RADIUS]; + } else if (rx < CORNER_RADIUS / r && ry >= h - CORNER_RADIUS / r) { + c = cornertable[rx * r + (h - 1 - ry) * r * CORNER_RADIUS]; + } else if (rx >= w - CORNER_RADIUS / r && ry < CORNER_RADIUS / r) { + c = cornertable[(w - 1 - rx) * r + ry * r * CORNER_RADIUS]; + } else if (rx >= w - CORNER_RADIUS / r && ry >= h - CORNER_RADIUS / r) { + c = cornertable[(w - 1 - rx) * r + (h - 1 - ry) * r * CORNER_RADIUS]; + } else { + c = 15; + } + int b = p / 2; + if (p % 2) { + buffer->buffer[b] |= c << 4; + } else { + buffer->buffer[b] |= (c); + } + } + } +} + #define UZLIB_WINDOW_SIZE (1 << 10) static void uzlib_prepare(struct uzlib_uncomp *decomp, uint8_t *window, diff --git a/core/embed/extmod/modtrezorui/display.h b/core/embed/extmod/modtrezorui/display.h index 4c946cf879..cbe82bb044 100644 --- a/core/embed/extmod/modtrezorui/display.h +++ b/core/embed/extmod/modtrezorui/display.h @@ -61,6 +61,8 @@ void display_clear(void); void display_bar(int x, int y, int w, int h, uint16_t c); void display_bar_radius(int x, int y, int w, int h, uint16_t c, uint16_t b, uint8_t r); +void display_bar_radius_buffer(int x, int y, int w, int h, uint8_t r, + buffer_text_t *buffer); bool display_toif_info(const uint8_t *buf, uint32_t len, uint16_t *out_w, uint16_t *out_h, toif_format_t *out_format); diff --git a/core/embed/extmod/modtrezorui/display_interface.h b/core/embed/extmod/modtrezorui/display_interface.h index 4b166fdc8d..5b9c3ca82a 100644 --- a/core/embed/extmod/modtrezorui/display_interface.h +++ b/core/embed/extmod/modtrezorui/display_interface.h @@ -43,6 +43,7 @@ int display_backlight(int val); void display_init(void); void display_reinit(void); +void display_sync(void); void display_refresh(void); const char *display_save(const char *prefix); void display_clear_save(void); diff --git a/core/embed/firmware/bl_check.c b/core/embed/firmware/bl_check.c index 444feb78c0..79279bc854 100644 --- a/core/embed/firmware/bl_check.c +++ b/core/embed/firmware/bl_check.c @@ -20,12 +20,14 @@ #include #include #include "blake2s.h" +#include "board_capabilities.h" #include "common.h" #include "flash.h" +#include "image.h" // symbols from bootloader.bin => bootloader.o -extern const uint32_t _binary_embed_firmware_bootloader_bin_start; -extern const uint32_t _binary_embed_firmware_bootloader_bin_size; +extern const void _binary_embed_firmware_bootloader_bin_start; +extern const void _binary_embed_firmware_bootloader_bin_size; /* static secbool known_bootloader(const uint8_t *hash, int len) { @@ -97,6 +99,48 @@ void check_and_replace_bootloader(void) { (const uint32_t *)&_binary_embed_firmware_bootloader_bin_start; const uint32_t len = (const uint32_t)&_binary_embed_firmware_bootloader_bin_size; + + const image_header *new_bld_hdr = read_image_header( + (uint8_t *)data, BOOTLOADER_IMAGE_MAGIC, BOOTLOADER_IMAGE_MAXSIZE); + + ensure(new_bld_hdr == (const image_header *)data ? sectrue : secfalse, + "Invalid embedded bootloader"); + + ensure(check_image_model(new_bld_hdr), "Incompatible embedded bootloader"); + + const image_header *current_bld_hdr = read_image_header( + bl_data, BOOTLOADER_IMAGE_MAGIC, BOOTLOADER_IMAGE_MAXSIZE); + + // cannot find valid header for current bootloader, something is wrong + ensure(current_bld_hdr == (const image_header *)bl_data ? sectrue : secfalse, + "Invalid bootloader header"); + + ensure(check_image_model(current_bld_hdr), "Incompatible bootloader found"); + + if (new_bld_hdr->monotonic < current_bld_hdr->monotonic) { + // reject downgrade + return; + } + + uint32_t board_name = get_board_name(); + if (board_name == 0 || strncmp((const char *)&board_name, "T2T1", 4) == 0) { + // no board capabilities, assume Model T + if ((strncmp((const char *)&new_bld_hdr->hw_model, "T2T1", 4) != 0) && + (new_bld_hdr->hw_model != 0)) { + // reject non-model T bootloader + // 0 represents pre-model check bootloader + ensure(secfalse, "Incompatible embedded bootloader"); + } + } + // at this point, due to the previous check_image_model call, we know that the + // new_bld_hdr is + // meant for the same model as this firmware, so we can check the board name + // against the firmware hw_model. + else if (board_name != HW_MODEL) { + // reject incompatible bootloader + ensure(secfalse, "Incompatible embedded bootloader"); + } + ensure(flash_erase(FLASH_SECTOR_BOOTLOADER), NULL); ensure(flash_unlock_write(), NULL); for (int i = 0; i < len / sizeof(uint32_t); i++) { diff --git a/core/embed/firmware/bootloaders/bootloader_T1B1.bin b/core/embed/firmware/bootloaders/bootloader_T1B1.bin new file mode 100644 index 0000000000..ac9d56f230 Binary files /dev/null and b/core/embed/firmware/bootloaders/bootloader_T1B1.bin differ diff --git a/core/embed/firmware/bootloaders/bootloader_T.bin b/core/embed/firmware/bootloaders/bootloader_T2T1.bin similarity index 100% rename from core/embed/firmware/bootloaders/bootloader_T.bin rename to core/embed/firmware/bootloaders/bootloader_T2T1.bin diff --git a/core/embed/firmware/header.S b/core/embed/firmware/header.S index 9d48890f39..3531e23955 100644 --- a/core/embed/firmware/header.S +++ b/core/embed/firmware/header.S @@ -13,7 +13,11 @@ g_header: .byte 'T','R','Z','F' // magic .word g_header_end - g_header // hdrlen +#ifdef TREZOR_MODEL_T .word 0 // expiry +#else + .word 1 // expiry +#endif .word _codelen // codelen .byte VERSION_MAJOR // vmajor .byte VERSION_MINOR // vminor @@ -23,7 +27,10 @@ g_header: .byte FIX_VERSION_MINOR // fix_vminor .byte FIX_VERSION_PATCH // fix_vpatch .byte FIX_VERSION_BUILD // fix_vbuild - . = . + 8 // reserved + .word HW_MODEL // type of the designated hardware + .byte HW_REVISION // revision of the designated hardware + .byte VERSION_MONOTONIC // monotonic version of the binary + . = . + 2 // reserved . = . + 512 // hash1 ... hash16 #if !defined TREZOR_MODEL_1 diff --git a/core/embed/firmware/main.c b/core/embed/firmware/main.c index c694fc85cf..0c68b00c1d 100644 --- a/core/embed/firmware/main.c +++ b/core/embed/firmware/main.c @@ -87,6 +87,8 @@ int main(void) { enable_systemview(); #endif + display_reinit(); + #if !defined TREZOR_MODEL_1 parse_boardloader_capabilities(); @@ -109,8 +111,6 @@ int main(void) { SCB->SHCSR |= (SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk); #endif - display_reinit(); - #if defined TREZOR_MODEL_1 button_init(); #endif diff --git a/core/embed/firmware/memory_T.ld b/core/embed/firmware/memory_T.ld index 50684eb807..91ee0f6e53 100644 --- a/core/embed/firmware/memory_T.ld +++ b/core/embed/firmware/memory_T.ld @@ -83,4 +83,9 @@ SECTIONS { . = 37K; /* this acts as a build time assertion that at least this much memory is available for heap use */ . = ABSOLUTE(sram_end); /* this explicitly sets the end of the heap */ } >SRAM + + .data_ccm : ALIGN(4) { + *(.no_dma_buffers*); + . = ALIGN(4); + } >CCMRAM } diff --git a/core/embed/firmware/version.h b/core/embed/firmware/version.h index 4e21240996..908621e6fa 100644 --- a/core/embed/firmware/version.h +++ b/core/embed/firmware/version.h @@ -7,3 +7,5 @@ #define FIX_VERSION_MINOR 4 #define FIX_VERSION_PATCH 0 #define FIX_VERSION_BUILD 0 + +#define VERSION_MONOTONIC 1 diff --git a/core/embed/prodtest/header.S b/core/embed/prodtest/header.S index 18a2bdaa4f..ce84552baa 100644 --- a/core/embed/prodtest/header.S +++ b/core/embed/prodtest/header.S @@ -10,7 +10,11 @@ g_header: .byte 'T','R','Z','F' // magic .word g_header_end - g_header // hdrlen +#ifdef TREZOR_MODEL_T .word 0 // expiry +#else + .word 1 // expiry +#endif .word _codelen // codelen .byte VERSION_MAJOR // vmajor .byte VERSION_MINOR // vminor @@ -20,7 +24,10 @@ g_header: .byte FIX_VERSION_MINOR // fix_vminor .byte FIX_VERSION_PATCH // fix_vpatch .byte FIX_VERSION_BUILD // fix_vbuild - . = . + 8 // reserved + .word HW_MODEL // type of the designated hardware + .byte HW_REVISION // revision of the designated hardware + .byte VERSION_MONOTONIC // monotonic version of the binary + . = . + 2 // reserved . = . + 512 // hash1 ... hash16 . = . + 415 // reserved .byte 0 // sigmask diff --git a/core/embed/prodtest/version.h b/core/embed/prodtest/version.h index f5eeee876a..2279c0cc06 100644 --- a/core/embed/prodtest/version.h +++ b/core/embed/prodtest/version.h @@ -7,3 +7,5 @@ #define FIX_VERSION_MINOR 1 #define FIX_VERSION_PATCH 0 #define FIX_VERSION_BUILD 0 + +#define VERSION_MONOTONIC 1 diff --git a/core/embed/reflash/header.S b/core/embed/reflash/header.S index 18a2bdaa4f..ce84552baa 100644 --- a/core/embed/reflash/header.S +++ b/core/embed/reflash/header.S @@ -10,7 +10,11 @@ g_header: .byte 'T','R','Z','F' // magic .word g_header_end - g_header // hdrlen +#ifdef TREZOR_MODEL_T .word 0 // expiry +#else + .word 1 // expiry +#endif .word _codelen // codelen .byte VERSION_MAJOR // vmajor .byte VERSION_MINOR // vminor @@ -20,7 +24,10 @@ g_header: .byte FIX_VERSION_MINOR // fix_vminor .byte FIX_VERSION_PATCH // fix_vpatch .byte FIX_VERSION_BUILD // fix_vbuild - . = . + 8 // reserved + .word HW_MODEL // type of the designated hardware + .byte HW_REVISION // revision of the designated hardware + .byte VERSION_MONOTONIC // monotonic version of the binary + . = . + 2 // reserved . = . + 512 // hash1 ... hash16 . = . + 415 // reserved .byte 0 // sigmask diff --git a/core/embed/reflash/version.h b/core/embed/reflash/version.h index f5eeee876a..2279c0cc06 100644 --- a/core/embed/reflash/version.h +++ b/core/embed/reflash/version.h @@ -7,3 +7,5 @@ #define FIX_VERSION_MINOR 1 #define FIX_VERSION_PATCH 0 #define FIX_VERSION_BUILD 0 + +#define VERSION_MONOTONIC 1 diff --git a/core/embed/rust/Cargo.toml b/core/embed/rust/Cargo.toml index 2bf223023d..aab0a8c012 100644 --- a/core/embed/rust/Cargo.toml +++ b/core/embed/rust/Cargo.toml @@ -8,8 +8,7 @@ build = "build.rs" [features] default = ["model_tt"] bitcoin_only = [] -model_tt = ["touch"] -model_t1 = ["buttons"] +model_tt = ["touch", "jpeg"] model_tr = ["buttons"] micropython = [] protobuf = ["micropython"] @@ -19,8 +18,9 @@ ui_debug = [] buttons = [] touch = [] clippy = [] +jpeg = [] debug = ["ui_debug"] -test = ["cc", "glob", "micropython", "protobuf", "ui", "ui_debug"] +test = ["cc", "glob", "micropython", "protobuf", "ui", "ui_debug", "dma2d"] [lib] crate-type = ["staticlib"] diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs index 6fec48da0a..916a6d3236 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -178,6 +178,7 @@ fn generate_micropython_bindings() { .allowlist_var("mp_type_dict") // fun .allowlist_type("mp_obj_fun_builtin_fixed_t") + .allowlist_var("mp_type_fun_builtin_0") .allowlist_var("mp_type_fun_builtin_1") .allowlist_var("mp_type_fun_builtin_2") .allowlist_var("mp_type_fun_builtin_3") @@ -287,13 +288,13 @@ fn generate_trezorhal_bindings() { .allowlist_function("display_text_width") .allowlist_function("display_bar") .allowlist_function("display_bar_radius") - .allowlist_function("display_icon") + .allowlist_function("display_bar_radius_buffer") .allowlist_function("display_image") - .allowlist_function("display_toif_info") .allowlist_function("display_loader") .allowlist_function("display_pixeldata") .allowlist_function("display_pixeldata_dirty") .allowlist_function("display_set_window") + .allowlist_function("display_sync") .allowlist_var("DISPLAY_CMD_ADDRESS") .allowlist_var("DISPLAY_DATA_ADDRESS") .allowlist_type("toif_format_t") @@ -320,16 +321,23 @@ fn generate_trezorhal_bindings() { .allowlist_function("hal_delay") .allowlist_function("hal_ticks_ms") // dma2d + .allowlist_function("dma2d_setup_4bpp") .allowlist_function("dma2d_setup_4bpp_over_4bpp") .allowlist_function("dma2d_setup_4bpp_over_16bpp") + .allowlist_function("dma2d_start") .allowlist_function("dma2d_start_blend") .allowlist_function("dma2d_wait_for_transfer") //buffers .allowlist_function("buffers_get_line_buffer_16bpp") .allowlist_function("buffers_get_line_buffer_4bpp") .allowlist_function("buffers_get_text_buffer") + .allowlist_function("buffers_get_jpeg_buffer") + .allowlist_function("buffers_get_jpeg_work_buffer") + .allowlist_function("buffers_get_blurring_buffer") .allowlist_var("text_buffer_height") - .allowlist_var("buffer_width"); + .allowlist_var("buffer_width") + //usb + .allowlist_function("usb_configured"); // Write the bindings to a file in the OUR_DIR. bindings .generate() diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index bbae778927..d6cd40356f 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -16,7 +16,11 @@ static void _librust_qstrs(void) { MP_QSTR_CONFIRMED; MP_QSTR_CANCELLED; MP_QSTR_INFO; + MP_QSTR_disable_animation; + MP_QSTR_jpeg_info; + MP_QSTR_jpeg_test; MP_QSTR_confirm_action; + MP_QSTR_confirm_homescreen; MP_QSTR_confirm_blob; MP_QSTR_confirm_properties; MP_QSTR_confirm_coinjoin; @@ -44,13 +48,20 @@ static void _librust_qstrs(void) { MP_QSTR_request_slip39; MP_QSTR_select_word; MP_QSTR_select_word_count; + MP_QSTR_show_busyscreen; MP_QSTR_show_group_share_success; + MP_QSTR_show_homescreen; + MP_QSTR_show_lockscreen; + MP_QSTR_draw_welcome_screen; MP_QSTR_show_remaining_shares; MP_QSTR_show_share_words; + MP_QSTR_show_progress; MP_QSTR_attach_timer_fn; MP_QSTR_touch_event; MP_QSTR_button_event; + MP_QSTR_progress_event; + MP_QSTR_usb_event; MP_QSTR_timer; MP_QSTR_paint; MP_QSTR_request_complete_repaint; @@ -90,10 +101,17 @@ static void _librust_qstrs(void) { MP_QSTR_min_count; MP_QSTR_max_count; MP_QSTR_items; + MP_QSTR_image; MP_QSTR_active; MP_QSTR_info_button; MP_QSTR_time_ms; MP_QSTR_app_name; MP_QSTR_icon_name; MP_QSTR_accounts; + MP_QSTR_indeterminate; + MP_QSTR_notification; + MP_QSTR_notification_level; + MP_QSTR_bootscreen; + MP_QSTR_skip_first_paint; + MP_QSTR_wrong_pin; } diff --git a/core/embed/rust/src/lib.rs b/core/embed/rust/src/lib.rs index f66b33fa9f..0a48f279c9 100644 --- a/core/embed/rust/src/lib.rs +++ b/core/embed/rust/src/lib.rs @@ -4,6 +4,7 @@ #![deny(unsafe_op_in_unsafe_fn)] #![allow(dead_code)] #![feature(lang_items)] +#![feature(optimize_attribute)] #[macro_use] extern crate num_derive; @@ -17,6 +18,7 @@ mod trezorhal; mod micropython; #[cfg(feature = "protobuf")] mod protobuf; +mod storage; mod time; #[cfg(feature = "ui_debug")] mod trace; diff --git a/core/embed/rust/src/micropython/gc.rs b/core/embed/rust/src/micropython/gc.rs index 466edb3052..3c0fe8e05c 100644 --- a/core/embed/rust/src/micropython/gc.rs +++ b/core/embed/rust/src/micropython/gc.rs @@ -38,6 +38,33 @@ impl Gc { } } +impl Gc<[T]> { + /// Allocate slice on the heap managed by the MicroPython garbage collector + /// and fill with default values. + pub fn new_slice(len: usize) -> Result { + let layout = Layout::array::(len).unwrap(); + // TODO: Assert that `layout.align()` is the same as the GC alignment. + // SAFETY: + // - Unfortunately we cannot respect `layout.align()` as MicroPython GC does + // not support custom alignment. + // - `ptr` is guaranteed to stay valid as long as it's reachable from the stack + // or the MicroPython heap. + // EXCEPTION: Returns null instead of raising. + unsafe { + let raw = ffi::gc_alloc(layout.size(), 0); + if raw.is_null() { + return Err(Error::AllocationFailed); + } + let typed: *mut T = raw.cast(); + for i in 0..len { + ptr::write(typed.add(i), T::default()); + } + let array_ptr = ptr::slice_from_raw_parts_mut(typed, len); + Ok(Self::from_raw(array_ptr as _)) + } + } +} + impl Gc { /// Construct a `Gc` from a raw pointer. /// diff --git a/core/embed/rust/src/micropython/macros.rs b/core/embed/rust/src/micropython/macros.rs index 557666c8e0..0ad558ee9e 100644 --- a/core/embed/rust/src/micropython/macros.rs +++ b/core/embed/rust/src/micropython/macros.rs @@ -1,3 +1,20 @@ +/// Create an object for an exported function taking no arguments. +macro_rules! obj_fn_0 { + ($f:expr) => {{ + #[allow(unused_unsafe)] + unsafe { + use $crate::micropython::ffi; + + ffi::mp_obj_fun_builtin_fixed_t { + base: ffi::mp_obj_base_t { + type_: &ffi::mp_type_fun_builtin_0, + }, + fun: ffi::_mp_obj_fun_builtin_fixed_t__bindgen_ty_1 { _0: Some($f) }, + } + } + }}; +} + /// Create an object for an exported function taking 1 arg. macro_rules! obj_fn_1 { ($f:expr) => {{ diff --git a/core/embed/rust/src/storage/mod.rs b/core/embed/rust/src/storage/mod.rs new file mode 100644 index 0000000000..29d4777ff6 --- /dev/null +++ b/core/embed/rust/src/storage/mod.rs @@ -0,0 +1,59 @@ +use crate::trezorhal::storage::{get, get_length}; + +pub const HOMESCREEN_MAX_SIZE: usize = 16384; + +const STORAGE_VERSION_01: u8 = 1; +const STORAGE_VERSION_02: u8 = 2; +const STORAGE_VERSION_CURRENT: u8 = STORAGE_VERSION_02; + +const FLAG_PUBLIC: u16 = 0x8000; +const FLAG_WRITE: u16 = 0xC000; +const APP_DEVICE: u16 = 0x0100; + +const DEVICE_ID: u16 = FLAG_PUBLIC | APP_DEVICE; +const VERSION: u16 = APP_DEVICE | 0x0001; +const MNEMONIC_SECRET: u16 = APP_DEVICE | 0x0002; +// NOTE: 0x03 key was used in the past for LANGUAGE. Not used anymore. +const LABEL: u16 = FLAG_PUBLIC | APP_DEVICE | 0x0004; +const USE_PASSPHRASE: u16 = APP_DEVICE | 0x0005; +const HOMESCREEN: u16 = FLAG_PUBLIC | APP_DEVICE | 0x0006; +const NEEDS_BACKUP: u16 = APP_DEVICE | 0x0007; +const FLAGS: u16 = APP_DEVICE | 0x0008; +const U2F_COUNTER_PRIVATE: u16 = APP_DEVICE | 0x0009; +const U2F_COUNTER: u16 = FLAG_PUBLIC | APP_DEVICE | 0x0009; +const PASSPHRASE_ALWAYS_ON_DEVICE: u16 = APP_DEVICE | 0x000A; +const UNFINISHED_BACKUP: u16 = APP_DEVICE | 0x000B; +const AUTOLOCK_DELAY_MS: u16 = APP_DEVICE | 0x000C; +const NO_BACKUP: u16 = APP_DEVICE | 0x000D; +const BACKUP_TYPE: u16 = APP_DEVICE | 0x000E; +const ROTATION: u16 = FLAG_PUBLIC | APP_DEVICE | 0x000F; +const SLIP39_IDENTIFIER: u16 = APP_DEVICE | 0x0010; +const SLIP39_ITERATION_EXPONENT: u16 = APP_DEVICE | 0x0011; +const SD_SALT_AUTH_KEY: u16 = FLAG_PUBLIC | APP_DEVICE | 0x0012; +const INITIALIZED: u16 = FLAG_PUBLIC | APP_DEVICE | 0x0013; +const SAFETY_CHECK_LEVEL: u16 = APP_DEVICE | 0x0014; +const EXPERIMENTAL_FEATURES: u16 = APP_DEVICE | 0x0015; + +pub fn get_avatar_len() -> Result { + let avatar_len_res = get_length(HOMESCREEN); + if let Ok(len) = avatar_len_res { + Ok(len) + } else { + Err(()) + } +} + +pub fn get_avatar(buffer: &mut [u8]) -> Result { + let avatar_len_res = get_length(HOMESCREEN); + + if let Ok(len) = avatar_len_res { + if len <= buffer.len() { + unwrap!(get(HOMESCREEN, buffer)); + Ok(len) + } else { + Err(()) + } + } else { + Err(()) + } +} diff --git a/core/embed/rust/src/trezorhal/bip39.rs b/core/embed/rust/src/trezorhal/bip39.rs index b205eef193..1dd0b534a9 100644 --- a/core/embed/rust/src/trezorhal/bip39.rs +++ b/core/embed/rust/src/trezorhal/bip39.rs @@ -38,6 +38,14 @@ pub fn complete_word(prefix: &str) -> Option<&'static str> { } } +pub fn options_num(prefix: &str) -> Option { + if prefix.is_empty() { + None + } else { + Some(Wordlist::all().filter_prefix(prefix).len()) + } +} + pub fn word_completion_mask(prefix: &str) -> u32 { // SAFETY: `mnemonic_word_completion_mask` shouldn't retain nor modify the // passed byte string, making the call safe. diff --git a/core/embed/rust/src/trezorhal/buffers.rs b/core/embed/rust/src/trezorhal/buffers.rs index 41879dac76..9b0f67e72e 100644 --- a/core/embed/rust/src/trezorhal/buffers.rs +++ b/core/embed/rust/src/trezorhal/buffers.rs @@ -1,9 +1,11 @@ use super::ffi; pub use ffi::{ - buffer_text_t as BufferText, line_buffer_16bpp_t as LineBuffer16Bpp, - line_buffer_4bpp_t as LineBuffer4Bpp, + buffer_blurring_t as BlurringBuffer, buffer_text_t as BufferText, + line_buffer_16bpp_t as LineBuffer16Bpp, line_buffer_4bpp_t as LineBuffer4Bpp, }; +#[cfg(feature = "jpeg")] +pub use ffi::{buffer_jpeg_t as BufferJpeg, buffer_jpeg_work_t as BufferJpegWork}; /// Returns a buffer for one line of 16bpp data /// @@ -43,3 +45,44 @@ pub unsafe fn get_text_buffer(idx: u16, clear: bool) -> &'static mut BufferText unwrap!(ptr.as_mut()) } } + +/// Returns a buffer for jpeg data +/// +/// # Safety +/// +/// This function is unsafe because the caller has to guarantee +/// that he doesn't use buffer on same index multiple times +#[cfg(feature = "jpeg")] +pub unsafe fn get_jpeg_buffer(idx: u16, clear: bool) -> &'static mut BufferJpeg { + unsafe { + let ptr = ffi::buffers_get_jpeg_buffer(idx, clear); + unwrap!(ptr.as_mut()) + } +} + +/// Returns a jpeg work buffer +/// +/// # Safety +/// +/// This function is unsafe because the caller has to guarantee +/// that he doesn't use buffer on same index multiple times +#[cfg(feature = "jpeg")] +pub unsafe fn get_jpeg_work_buffer(idx: u16, clear: bool) -> &'static mut BufferJpegWork { + unsafe { + let ptr = ffi::buffers_get_jpeg_work_buffer(idx, clear); + unwrap!(ptr.as_mut()) + } +} + +/// Returns a buffer for blurring data +/// +/// # Safety +/// +/// This function is unsafe because the caller has to guarantee +/// that he doesn't use buffer on same index multiple times +pub unsafe fn get_blurring_buffer(idx: u16, clear: bool) -> &'static mut BlurringBuffer { + unsafe { + let ptr = ffi::buffers_get_blurring_buffer(idx, clear); + unwrap!(ptr.as_mut()) + } +} diff --git a/core/embed/rust/src/trezorhal/display.rs b/core/embed/rust/src/trezorhal/display.rs index d23b603b1c..17e9c51284 100644 --- a/core/embed/rust/src/trezorhal/display.rs +++ b/core/embed/rust/src/trezorhal/display.rs @@ -1,11 +1,10 @@ use super::ffi; use core::ptr; use cty::c_int; -use num_traits::FromPrimitive; use crate::trezorhal::buffers::BufferText; -#[derive(PartialEq, Debug, Eq, FromPrimitive)] +#[derive(PartialEq, Debug, Eq, FromPrimitive, Clone, Copy)] pub enum ToifFormat { FullColorBE = ffi::toif_format_t_TOIF_FULL_COLOR_BE as _, GrayScaleOH = ffi::toif_format_t_TOIF_GRAYSCALE_OH as _, @@ -13,12 +12,6 @@ pub enum ToifFormat { GrayScaleEH = ffi::toif_format_t_TOIF_GRAYSCALE_EH as _, } -pub struct ToifInfo { - pub width: u16, - pub height: u16, - pub format: ToifFormat, -} - pub fn backlight(val: i32) -> i32 { unsafe { ffi::display_backlight(val) } } @@ -97,18 +90,9 @@ pub fn bar_radius(x: i16, y: i16, w: i16, h: i16, fgcolor: u16, bgcolor: u16, ra } } -pub fn icon(x: i16, y: i16, w: i16, h: i16, data: &[u8], fgcolor: u16, bgcolor: u16) { +pub fn bar_radius_buffer(x: i16, y: i16, w: i16, h: i16, radius: u8, buffer: &mut BufferText) { unsafe { - ffi::display_icon( - x.into(), - y.into(), - w.into(), - h.into(), - data.as_ptr() as _, - data.len() as _, - fgcolor, - bgcolor, - ) + ffi::display_bar_radius_buffer(x.into(), y.into(), w.into(), h.into(), radius, buffer as _) } } @@ -125,31 +109,6 @@ pub fn image(x: i16, y: i16, w: i16, h: i16, data: &[u8]) { } } -pub fn toif_info(data: &[u8]) -> Result { - let mut width: cty::uint16_t = 0; - let mut height: cty::uint16_t = 0; - let mut format: ffi::toif_format_t = ffi::toif_format_t_TOIF_FULL_COLOR_BE; - if unsafe { - ffi::display_toif_info( - data.as_ptr() as _, - data.len() as _, - &mut width, - &mut height, - &mut format, - ) - } { - let format = ToifFormat::from_usize(format as usize).unwrap_or(ToifFormat::FullColorBE); - - Ok(ToifInfo { - width, - height, - format, - }) - } else { - Err(()) - } -} - pub fn loader( progress: u16, indeterminate: bool, @@ -210,3 +169,9 @@ pub fn get_offset() -> (i16, i16) { (x as i16, y as i16) } } + +pub fn sync() { + unsafe { + ffi::display_sync(); + } +} diff --git a/core/embed/rust/src/trezorhal/dma2d.rs b/core/embed/rust/src/trezorhal/dma2d.rs index ed56e3eacc..0de4daa17d 100644 --- a/core/embed/rust/src/trezorhal/dma2d.rs +++ b/core/embed/rust/src/trezorhal/dma2d.rs @@ -1,5 +1,9 @@ use super::ffi; +pub fn dma2d_setup_4bpp(fg_color: u16, bg_color: u16) { + unsafe { ffi::dma2d_setup_4bpp(fg_color, bg_color) } +} + pub fn dma2d_setup_4bpp_over_4bpp(fg_color: u16, bg_color: u16, overlay_color: u16) { unsafe { ffi::dma2d_setup_4bpp_over_4bpp(fg_color, bg_color, overlay_color) } } @@ -8,6 +12,16 @@ pub fn dma2d_setup_4bpp_over_16bpp(overlay_color: u16) { unsafe { ffi::dma2d_setup_4bpp_over_16bpp(overlay_color) } } +pub fn dma2d_start(buffer: &[u8], pixels: i16) { + unsafe { + ffi::dma2d_start( + buffer.as_ptr() as _, + ffi::DISPLAY_DATA_ADDRESS as _, + pixels as _, + ); + } +} + pub fn dma2d_start_blend(overlay_buffer: &[u8], bg_buffer: &[u8], pixels: i16) { unsafe { ffi::dma2d_start_blend( diff --git a/core/embed/rust/src/trezorhal/ffi.rs b/core/embed/rust/src/trezorhal/ffi.rs index 67269896bd..f8fcf3a49a 100644 --- a/core/embed/rust/src/trezorhal/ffi.rs +++ b/core/embed/rust/src/trezorhal/ffi.rs @@ -1,5 +1,6 @@ #![allow(non_camel_case_types)] #![allow(non_upper_case_globals)] +#![allow(clippy::upper_case_acronyms)] #![allow(non_snake_case)] #![allow(dead_code)] diff --git a/core/embed/rust/src/trezorhal/mod.rs b/core/embed/rust/src/trezorhal/mod.rs index 496a0f2917..9696c53dcd 100644 --- a/core/embed/rust/src/trezorhal/mod.rs +++ b/core/embed/rust/src/trezorhal/mod.rs @@ -13,6 +13,7 @@ pub mod random; pub mod rgb_led; pub mod slip39; pub mod storage; +pub mod usb; pub mod uzlib; pub mod buffers; diff --git a/core/embed/rust/src/trezorhal/usb.rs b/core/embed/rust/src/trezorhal/usb.rs new file mode 100644 index 0000000000..241e0fc3e0 --- /dev/null +++ b/core/embed/rust/src/trezorhal/usb.rs @@ -0,0 +1,5 @@ +use super::ffi; + +pub fn usb_configured() -> bool { + unsafe { ffi::usb_configured() == ffi::sectrue } +} diff --git a/core/embed/rust/src/ui/component/base.rs b/core/embed/rust/src/ui/component/base.rs index f4e0c0d3ca..fed6b792dd 100644 --- a/core/embed/rust/src/ui/component/base.rs +++ b/core/embed/rust/src/ui/component/base.rs @@ -15,6 +15,7 @@ use crate::{ use crate::ui::event::ButtonEvent; #[cfg(feature = "touch")] use crate::ui::event::TouchEvent; +use crate::ui::event::USBEvent; /// Type used by components that do not return any messages. /// @@ -104,6 +105,16 @@ impl Child { } result } + + /// Do not draw on screen until an event requests paint. This is used by + /// homescreens to avoid flickering when workflow restart happens. + pub fn skip_paint(&mut self) { + self.marked_for_paint = false; + } + + pub fn will_paint(&self) -> bool { + self.marked_for_paint + } } impl Component for Child @@ -330,14 +341,17 @@ where } #[derive(Copy, Clone, PartialEq, Eq)] -pub enum Event { +pub enum Event<'a> { #[cfg(feature = "buttons")] Button(ButtonEvent), #[cfg(feature = "touch")] Touch(TouchEvent), + USB(USBEvent), /// Previously requested timer was triggered. This invalidates the timer /// token (another timer has to be requested). Timer(TimerToken), + /// Advance progress bar. Progress screens only. + Progress(u16, &'a str), /// Component has been attached to component tree. This event is sent once /// before any other events. Attach, diff --git a/core/embed/rust/src/ui/component/image.rs b/core/embed/rust/src/ui/component/image.rs index 1538cc9646..4f18b7270b 100644 --- a/core/embed/rust/src/ui/component/image.rs +++ b/core/embed/rust/src/ui/component/image.rs @@ -1,22 +1,35 @@ -use crate::ui::{ - component::{Component, Event, EventCtx, Never}, - display, - display::{toif_info, Color}, - geometry::{Alignment, Offset, Point, Rect}, +use crate::{ + trezorhal::display::{image, ToifFormat}, + ui::{ + component::{Component, Event, EventCtx, Never}, + display, + display::{toif::Toif, Color, Icon}, + geometry::{Alignment2D, Offset, Point, Rect, CENTER}, + }, }; +#[derive(PartialEq, Eq, Clone, Copy)] pub struct Image { - image: &'static [u8], + pub toif: Toif, area: Rect, } impl Image { - pub fn new(image: &'static [u8]) -> Self { + pub fn new(data: &'static [u8]) -> Self { + let toif = unwrap!(Toif::new(data)); + assert!(toif.format() == ToifFormat::FullColorLE); Self { - image, + toif, area: Rect::zero(), } } + + /// Display the icon with baseline Point, aligned according to the + /// `alignment` argument. + pub fn draw(&self, baseline: Point, alignment: Alignment2D) { + let r = Rect::snap(baseline, self.toif.size(), alignment); + image(r.x0, r.y0, r.width(), r.height(), self.toif.zdata()); + } } impl Component for Image { @@ -32,13 +45,14 @@ impl Component for Image { } fn paint(&mut self) { - display::image(self.area.center(), self.image) + self.draw(self.area.center(), CENTER); } fn bounds(&self, sink: &mut dyn FnMut(Rect)) { - if let Some((size, _)) = display::toif_info(self.image) { - sink(Rect::from_center_and_size(self.area.center(), size)); - } + sink(Rect::from_center_and_size( + self.area.center(), + self.toif.size(), + )); } } @@ -51,8 +65,8 @@ impl crate::trace::Trace for Image { } pub struct BlendedImage { - bg: &'static [u8], - fg: &'static [u8], + bg: Icon, + fg: Icon, bg_color: Color, fg_color: Color, area_color: Color, @@ -61,13 +75,7 @@ pub struct BlendedImage { } impl BlendedImage { - pub fn new( - bg: &'static [u8], - fg: &'static [u8], - bg_color: Color, - fg_color: Color, - area_color: Color, - ) -> Self { + pub fn new(bg: Icon, fg: Icon, bg_color: Color, fg_color: Color, area_color: Color) -> Self { Self { bg, fg, @@ -93,15 +101,12 @@ impl Component for BlendedImage { type Msg = Never; fn place(&mut self, bounds: Rect) -> Rect { - let (bg_size, _) = unwrap!(toif_info(self.bg)); + self.bg_top_left = self.bg.toif.size().snap(bounds.center(), CENTER); - self.bg_top_left = bg_size.snap(bounds.center(), Alignment::Center, Alignment::Center); + let ft_top_left = self.fg.toif.size().snap(bounds.center(), CENTER); + self.fg_offset = ft_top_left - self.bg_top_left; - if let Some((fg_size, _)) = toif_info(self.fg) { - let ft_top_left = fg_size.snap(bounds.center(), Alignment::Center, Alignment::Center); - self.fg_offset = ft_top_left - self.bg_top_left; - } - Rect::from_top_left_and_size(self.bg_top_left, bg_size) + Rect::from_top_left_and_size(self.bg_top_left, self.bg.toif.size()) } fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option { @@ -113,9 +118,10 @@ impl Component for BlendedImage { } fn bounds(&self, sink: &mut dyn FnMut(Rect)) { - if let Some((size, _)) = display::toif_info(self.bg) { - sink(Rect::from_top_left_and_size(self.bg_top_left, size)); - } + sink(Rect::from_top_left_and_size( + self.bg_top_left, + self.bg.toif.size(), + )); } } diff --git a/core/embed/rust/src/ui/component/label.rs b/core/embed/rust/src/ui/component/label.rs index 9a55fe69b2..f49097ce43 100644 --- a/core/embed/rust/src/ui/component/label.rs +++ b/core/embed/rust/src/ui/component/label.rs @@ -1,5 +1,3 @@ -use core::ops::Deref; - use crate::ui::{ component::{Component, Event, EventCtx, Never}, display::Font, @@ -15,7 +13,7 @@ pub struct Label { impl Label where - T: Deref, + T: AsRef, { pub fn new(text: T, align: Alignment, style: TextStyle) -> Self { Self { @@ -54,20 +52,24 @@ where pub fn max_size(&self) -> Offset { let font = self.font(); - Offset::new(font.text_width(&self.text), font.text_max_height()) + Offset::new(font.text_width(self.text.as_ref()), font.text_max_height()) } } impl Component for Label where - T: Deref, + T: AsRef, { type Msg = Never; fn place(&mut self, bounds: Rect) -> Rect { - let line_bounds = bounds.with_height(self.font().text_max_height()); - self.layout = self.layout.with_bounds(line_bounds); - line_bounds + let height = self + .layout + .with_bounds(bounds) + .fit_text(self.text.as_ref()) + .height(); + self.layout = self.layout.with_bounds(bounds.with_height(height)); + self.layout.bounds } fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option { @@ -75,7 +77,7 @@ where } fn paint(&mut self) { - self.layout.render_text(&self.text); + self.layout.render_text(self.text.as_ref()); } fn bounds(&self, sink: &mut dyn FnMut(Rect)) { @@ -86,9 +88,9 @@ where #[cfg(feature = "ui_debug")] impl crate::trace::Trace for Label where - T: Deref, + T: AsRef, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.string(&self.text) + t.string(self.text.as_ref()) } } diff --git a/core/embed/rust/src/ui/component/marquee.rs b/core/embed/rust/src/ui/component/marquee.rs new file mode 100644 index 0000000000..d3559521d0 --- /dev/null +++ b/core/embed/rust/src/ui/component/marquee.rs @@ -0,0 +1,240 @@ +use crate::{ + time::{Duration, Instant}, + ui::{ + animation::Animation, + component::{Component, Event, EventCtx, Never, TimerToken}, + display, + display::{Color, Font}, + geometry::Rect, + }, +}; + +const MILLIS_PER_LETTER_M: u32 = 300; + +enum State { + Initial, + Left(Animation), + PauseLeft, + Right(Animation), + PauseRight, +} + +pub struct Marquee { + area: Rect, + pause_token: Option, + min_offset: i16, + max_offset: i16, + state: State, + text: T, + font: Font, + fg: Color, + bg: Color, + duration: Duration, + pause: Duration, +} + +impl Marquee +where + T: AsRef, +{ + pub fn new(text: T, font: Font, fg: Color, bg: Color) -> Self { + Self { + area: Rect::zero(), + pause_token: None, + min_offset: 0, + max_offset: 0, + state: State::Initial, + text, + font, + fg, + bg, + duration: Duration::from_millis(2000), + pause: Duration::from_millis(1000), + } + } + + pub fn start(&mut self, ctx: &mut EventCtx, now: Instant) { + if let State::Initial = self.state { + let text_width = self.font.text_width(self.text.as_ref()); + let max_offset = self.area.width() - text_width; + + self.min_offset = 0; + self.max_offset = max_offset; + + let anim = Animation::new(self.min_offset, max_offset, self.duration, now); + + self.state = State::Left(anim); + + // The animation is starting, request an animation frame event. + ctx.request_anim_frame(); + + // We don't have to wait for the animation frame event with the first paint, + // let's do that now. + ctx.request_paint(); + } + } + + pub fn reset(&mut self) { + self.state = State::Initial; + } + + pub fn animation(&self) -> Option<&Animation> { + match &self.state { + State::Initial => None, + State::Left(a) => Some(a), + State::PauseLeft => None, + State::Right(a) => Some(a), + State::PauseRight => None, + } + } + + pub fn is_at_right(&self, now: Instant) -> bool { + if let Some(p) = self.progress(now) { + return p == self.min_offset; + } + false + } + + pub fn is_at_left(&self, now: Instant) -> bool { + if let Some(p) = self.progress(now) { + return p == self.max_offset; + } + false + } + + pub fn progress(&self, now: Instant) -> Option { + self.animation().map(|a| a.value(now)) + } + + pub fn is_animating(&self) -> bool { + self.animation().is_some() + } + + pub fn paint_anim(&mut self, offset: i16) { + display::marquee( + self.area, + self.text.as_ref(), + offset, + self.font, + self.fg, + self.bg, + ); + } +} + +impl Component for Marquee +where + T: AsRef, +{ + type Msg = Never; + + fn place(&mut self, bounds: Rect) -> Rect { + let base_width = self.font.text_width("M"); + let text_width = self.font.text_width(self.text.as_ref()); + let area_width = bounds.width(); + + let shift_width = if area_width > text_width { + area_width - text_width + } else { + text_width - area_width + }; + + let mut duration = (MILLIS_PER_LETTER_M * shift_width as u32) / base_width as u32; + if duration < MILLIS_PER_LETTER_M { + duration = MILLIS_PER_LETTER_M; + } + + self.duration = Duration::from_millis(duration); + self.area = bounds; + self.area + } + + fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { + let now = Instant::now(); + + if let Event::Timer(token) = event { + if self.pause_token == Some(token) { + match self.state { + State::PauseLeft => { + let anim = + Animation::new(self.max_offset, self.min_offset, self.duration, now); + self.state = State::Right(anim); + } + State::PauseRight => { + let anim = + Animation::new(self.min_offset, self.max_offset, self.duration, now); + self.state = State::Left(anim); + } + _ => {} + } + // We have something to paint, so request to be painted in the next pass. + ctx.request_paint(); + // There is further progress in the animation, request an animation frame event. + ctx.request_anim_frame(); + } + + if token == EventCtx::ANIM_FRAME_TIMER { + if self.is_animating() { + // We have something to paint, so request to be painted in the next pass. + ctx.request_paint(); + // There is further progress in the animation, request an animation frame + // event. + ctx.request_anim_frame(); + } + + match self.state { + State::Right(_) => { + if self.is_at_right(now) { + self.pause_token = Some(ctx.request_timer(self.pause)); + self.state = State::PauseRight; + } + } + State::Left(_) => { + if self.is_at_left(now) { + self.pause_token = Some(ctx.request_timer(self.pause)); + self.state = State::PauseLeft; + } + } + _ => {} + } + } + } + None + } + + fn paint(&mut self) { + let now = Instant::now(); + + match self.state { + State::Initial => { + self.paint_anim(0); + } + State::PauseRight => { + self.paint_anim(self.min_offset); + } + State::PauseLeft => { + self.paint_anim(self.max_offset); + } + _ => { + let progress = self.progress(now); + if let Some(done) = progress { + self.paint_anim(done as i16); + } else { + self.paint_anim(0); + } + } + } + } +} + +#[cfg(feature = "ui_debug")] +impl crate::trace::Trace for Marquee +where + T: AsRef, +{ + fn trace(&self, d: &mut dyn crate::trace::Tracer) { + d.open("Marquee"); + d.field("text", &self.text.as_ref()); + d.close(); + } +} diff --git a/core/embed/rust/src/ui/component/mod.rs b/core/embed/rust/src/ui/component/mod.rs index 71f696b467..96aa50b374 100644 --- a/core/embed/rust/src/ui/component/mod.rs +++ b/core/embed/rust/src/ui/component/mod.rs @@ -6,6 +6,7 @@ pub mod empty; pub mod image; pub mod label; pub mod map; +pub mod marquee; pub mod maybe; pub mod pad; pub mod paginated; @@ -17,9 +18,9 @@ pub mod timeout; pub use base::{Child, Component, ComponentExt, Event, EventCtx, Never, TimerToken}; pub use border::Border; pub use empty::Empty; -pub use image::Image; pub use label::Label; pub use map::Map; +pub use marquee::Marquee; pub use maybe::Maybe; pub use pad::Pad; pub use paginated::{PageMsg, Paginate}; diff --git a/core/embed/rust/src/ui/component/painter.rs b/core/embed/rust/src/ui/component/painter.rs index 7a572b10d2..6c6569d4cd 100644 --- a/core/embed/rust/src/ui/component/painter.rs +++ b/core/embed/rust/src/ui/component/painter.rs @@ -1,7 +1,9 @@ +#[cfg(feature = "jpeg")] +use crate::ui::geometry::Offset; use crate::ui::{ - component::{Component, Event, EventCtx, Never}, + component::{image::Image, Component, Event, EventCtx, Never}, display, - geometry::Rect, + geometry::{Rect, CENTER}, }; pub struct Painter { @@ -60,7 +62,18 @@ where Painter::new(f) } -pub fn image_painter(image: &'static [u8]) -> Painter { - let f = move |area: Rect| display::image(area.center(), image); +pub fn image_painter(image: Image) -> Painter { + let f = move |area: Rect| image.draw(area.center(), CENTER); + Painter::new(f) +} + +#[cfg(feature = "jpeg")] +pub fn jpeg_painter<'a>( + image: impl Fn() -> &'a [u8], + size: Offset, + scale: u8, +) -> Painter { + let off = Offset::new(size.x / (2 << scale), size.y / (2 << scale)); + let f = move |area: Rect| display::tjpgd::jpeg(image(), area.center() - off, scale); Painter::new(f) } diff --git a/core/embed/rust/src/ui/component/text/paragraphs.rs b/core/embed/rust/src/ui/component/text/paragraphs.rs index f4e693493a..73897bdc04 100644 --- a/core/embed/rust/src/ui/component/text/paragraphs.rs +++ b/core/embed/rust/src/ui/component/text/paragraphs.rs @@ -2,8 +2,8 @@ use heapless::Vec; use crate::ui::{ component::{Component, Event, EventCtx, Never, Paginate}, - display, - geometry::{Alignment, Insets, LinearPlacement, Offset, Point, Rect}, + display::toif::Icon, + geometry::{Alignment, Insets, LinearPlacement, Offset, Point, Rect, TOP_LEFT}, }; use super::layout::{LayoutFit, TextLayout, TextStyle}; @@ -89,6 +89,10 @@ where self } + pub fn inner(&self) -> &T { + &self.source + } + pub fn inner_mut(&mut self) -> &mut T { &mut self.source } @@ -501,8 +505,8 @@ pub struct Checklist { area: Rect, paragraphs: Paragraphs, current: usize, - icon_current: &'static [u8], - icon_done: &'static [u8], + icon_current: Icon, + icon_done: Icon, } impl Checklist { @@ -511,8 +515,8 @@ impl Checklist { const CURRENT_OFFSET: Offset = Offset::new(2, 3); pub fn from_paragraphs( - icon_current: &'static [u8], - icon_done: &'static [u8], + icon_current: Icon, + icon_done: Icon, current: usize, paragraphs: Paragraphs, ) -> Self { @@ -525,11 +529,11 @@ impl Checklist { } } - fn paint_icon(&self, layout: &TextLayout, icon: &'static [u8], offset: Offset) { + fn paint_icon(&self, layout: &TextLayout, icon: Icon, offset: Offset) { let top_left = Point::new(self.area.x0, layout.bounds.y0); - display::icon_top_left( + icon.draw( top_left + offset, - icon, + TOP_LEFT, layout.style.text_color, layout.style.background_color, ); diff --git a/core/embed/rust/src/ui/constant.rs b/core/embed/rust/src/ui/constant.rs index 2216fffe64..91fd83f704 100644 --- a/core/embed/rust/src/ui/constant.rs +++ b/core/embed/rust/src/ui/constant.rs @@ -1,12 +1,6 @@ //! Reexporting the `constant` module according to the //! current feature (Trezor model) -#[cfg(all( - feature = "model_t1", - not(feature = "model_tr"), - not(feature = "model_tt") -))] -pub use super::model_t1::constant::*; #[cfg(all(feature = "model_tr", not(feature = "model_tt")))] pub use super::model_tr::constant::*; #[cfg(feature = "model_tt")] diff --git a/core/embed/rust/src/ui/display/loader.rs b/core/embed/rust/src/ui/display/loader/circular.rs similarity index 87% rename from core/embed/rust/src/ui/display/loader.rs rename to core/embed/rust/src/ui/display/loader/circular.rs index 104527d763..000905e678 100644 --- a/core/embed/rust/src/ui/display/loader.rs +++ b/core/embed/rust/src/ui/display/loader/circular.rs @@ -1,12 +1,8 @@ -use crate::{ - trezorhal::uzlib::UzlibContext, - ui::{ - constant, display, - display::{Color, ToifFormat}, - geometry::{Offset, Point, Rect}, - }, +use crate::ui::{ + constant, display, + display::Color, + geometry::{Offset, Point, Rect}, }; -use core::slice::from_raw_parts; #[cfg(feature = "dma2d")] use crate::trezorhal::{ @@ -16,12 +12,9 @@ use crate::trezorhal::{ use crate::ui::{ constant::{screen, LOADER_OUTER}, - display::toif_info_ensure, + display::toif::Icon, }; -pub const LOADER_MIN: u16 = 0; -pub const LOADER_MAX: u16 = 1000; - const LOADER_SIZE: i32 = (LOADER_OUTER * 2.0) as i32; const OUTER: f32 = constant::LOADER_OUTER; @@ -35,22 +28,21 @@ const INNER_OUTER_ANTI: i32 = ((INNER + 2.5) * (INNER + 2.5)) as i32; const OUTER_OUT_ANTI: i32 = ((OUTER - 1.5) * (OUTER - 1.5)) as i32; const OUTER_MAX: i32 = ((OUTER - 0.5) * (OUTER - 0.5)) as i32; -pub fn loader_uncompress( +pub fn loader_circular_uncompress( y_offset: i16, fg_color: Color, bg_color: Color, progress: u16, indeterminate: bool, - icon: Option<(&[u8], Color)>, + icon: Option<(Icon, Color)>, ) { const ICON_MAX_SIZE: i16 = constant::LOADER_ICON_MAX_SIZE; - if let Some((data, color)) = icon { - let (toif_size, toif_data) = toif_info_ensure(data, ToifFormat::GrayScaleEH); + if let Some((icon, color)) = icon { + let toif_size = icon.toif.size(); if toif_size.x <= ICON_MAX_SIZE && toif_size.y <= ICON_MAX_SIZE { let mut icon_data = [0_u8; ((ICON_MAX_SIZE * ICON_MAX_SIZE) / 2) as usize]; - let mut ctx = UzlibContext::new(toif_data, None); - unwrap!(ctx.uncompress(&mut icon_data), "Decompression failed"); + icon.toif.uncompress(&mut icon_data); let i = Some((icon_data.as_ref(), color, toif_size)); loader_rust(y_offset, fg_color, bg_color, progress, indeterminate, i); } else { @@ -61,29 +53,24 @@ pub fn loader_uncompress( } } -#[no_mangle] -pub extern "C" fn loader_uncompress_r( - y_offset: cty::int32_t, - fg_color: cty::uint16_t, - bg_color: cty::uint16_t, - icon_color: cty::uint16_t, - progress: cty::int32_t, - indeterminate: cty::int32_t, - icon_data: cty::uintptr_t, - icon_data_size: cty::uint32_t, +pub fn loader_circular( + progress: u16, + y_offset: i16, + fg_color: Color, + bg_color: Color, + icon: Option<(Icon, Color)>, ) { - let fg = Color::from_u16(fg_color); - let bg = Color::from_u16(bg_color); - let ic_color = Color::from_u16(icon_color); + loader_circular_uncompress(y_offset, fg_color, bg_color, progress, false, icon); +} - let i = if icon_data != 0 { - let data_slice = unsafe { from_raw_parts(icon_data as _, icon_data_size as _) }; - Some((data_slice, ic_color)) - } else { - None - }; - - loader_uncompress(y_offset as _, fg, bg, progress as _, indeterminate != 0, i); +pub fn loader_circular_indeterminate( + progress: u16, + y_offset: i16, + fg_color: Color, + bg_color: Color, + icon: Option<(Icon, Color)>, +) { + loader_circular_uncompress(y_offset, fg_color, bg_color, progress, true, icon); } #[inline(always)] @@ -374,23 +361,3 @@ pub fn loader_rust( dma2d_wait_for_transfer(); } - -pub fn loader( - progress: u16, - y_offset: i16, - fg_color: Color, - bg_color: Color, - icon: Option<(&[u8], Color)>, -) { - loader_uncompress(y_offset, fg_color, bg_color, progress, false, icon); -} - -pub fn loader_indeterminate( - progress: u16, - y_offset: i16, - fg_color: Color, - bg_color: Color, - icon: Option<(&[u8], Color)>, -) { - loader_uncompress(y_offset, fg_color, bg_color, progress, true, icon); -} diff --git a/core/embed/rust/src/ui/display/loader/mod.rs b/core/embed/rust/src/ui/display/loader/mod.rs new file mode 100644 index 0000000000..12d445a409 --- /dev/null +++ b/core/embed/rust/src/ui/display/loader/mod.rs @@ -0,0 +1,68 @@ +mod circular; +mod rectangular; +mod starry; + +use crate::ui::display::{Color, Icon}; +use core::slice::from_raw_parts; + +#[cfg(feature = "model_tt")] +use crate::ui::display::loader::circular::{ + loader_circular as determinate, loader_circular_indeterminate as indeterminate, +}; +#[cfg(not(feature = "model_tt"))] +use crate::ui::display::loader::rectangular::loader_rectangular as determinate; +#[cfg(not(feature = "model_tt"))] +use crate::ui::display::loader::starry::loader_starry_indeterminate as indeterminate; + +pub const LOADER_MIN: u16 = 0; +pub const LOADER_MAX: u16 = 1000; + +pub fn loader( + progress: u16, + y_offset: i16, + fg_color: Color, + bg_color: Color, + icon: Option<(Icon, Color)>, +) { + determinate(progress, y_offset, fg_color, bg_color, icon); +} + +pub fn loader_indeterminate( + progress: u16, + y_offset: i16, + fg_color: Color, + bg_color: Color, + icon: Option<(Icon, Color)>, +) { + indeterminate(progress, y_offset, fg_color, bg_color, icon); +} + +//TODO: remove when loader is no longer called from C +#[no_mangle] +pub extern "C" fn loader_uncompress_r( + y_offset: cty::int32_t, + fg_color: cty::uint16_t, + bg_color: cty::uint16_t, + icon_color: cty::uint16_t, + progress: cty::int32_t, + indeterminate: cty::int32_t, + icon_data: cty::uintptr_t, + icon_data_size: cty::uint32_t, +) { + let fg = Color::from_u16(fg_color); + let bg = Color::from_u16(bg_color); + let ic_color = Color::from_u16(icon_color); + + let i = if icon_data != 0 { + let data_slice = unsafe { from_raw_parts(icon_data as _, icon_data_size as _) }; + Some((Icon::new(data_slice), ic_color)) + } else { + None + }; + + if indeterminate == 0 { + loader(progress as _, y_offset as _, fg, bg, i); + } else { + loader_indeterminate(progress as _, y_offset as _, fg, bg, i); + } +} diff --git a/core/embed/rust/src/ui/display/loader/rectangular.rs b/core/embed/rust/src/ui/display/loader/rectangular.rs new file mode 100644 index 0000000000..7ad16c57f0 --- /dev/null +++ b/core/embed/rust/src/ui/display/loader/rectangular.rs @@ -0,0 +1,25 @@ +use crate::ui::{ + constant::{screen, LOADER_INNER}, + display, + display::{Color, Icon}, + geometry::{Offset, Rect}, +}; + +pub fn loader_rectangular( + progress: u16, + y_offset: i16, + fg_color: Color, + bg_color: Color, + icon: Option<(Icon, Color)>, +) { + let area = Rect::from_center_and_size(screen().center(), Offset::uniform(LOADER_INNER as _)) + .translate(Offset::y(y_offset)); + + display::rect_rounded2_partial( + area, + fg_color, + bg_color, + (100 * progress as u32 / 1000) as i16, + icon, + ); +} diff --git a/core/embed/rust/src/ui/display/loader/starry.rs b/core/embed/rust/src/ui/display/loader/starry.rs new file mode 100644 index 0000000000..2dbdd2f5f4 --- /dev/null +++ b/core/embed/rust/src/ui/display/loader/starry.rs @@ -0,0 +1,67 @@ +use crate::ui::{ + constant::{screen, LOADER_OUTER}, + display::{rect_fill, rect_fill_rounded, rect_fill_rounded1, Color, Icon}, + geometry::{Offset, Point, Rect, CENTER}, +}; +use core::f32::consts::SQRT_2; + +const SIZE_SMALL: i16 = 2; +const SIZE_MEDIUM: i16 = 4; +const SIZE_LARGE: i16 = 6; +const RADIUS: i16 = 13; +const DIAGONAL: i16 = ((RADIUS as f32 * SQRT_2) / 2_f32) as i16; + +fn star_small(center: Point, fg: Color, _bg: Color) { + let r = Rect::from_center_and_size(center, Offset::uniform(SIZE_SMALL)); + rect_fill(r, fg); +} + +fn star_medium(center: Point, fg: Color, bg: Color) { + let r = Rect::from_center_and_size(center, Offset::uniform(SIZE_MEDIUM)); + rect_fill_rounded1(r, fg, bg); +} + +fn star_large(center: Point, fg: Color, bg: Color) { + let r = Rect::from_center_and_size(center, Offset::uniform(SIZE_LARGE)); + rect_fill_rounded(r, fg, bg, 2); +} + +pub fn loader_starry_indeterminate( + progress: u16, + y_offset: i16, + fg_color: Color, + bg_color: Color, + icon: Option<(Icon, Color)>, +) { + let area = Rect::from_center_and_size(screen().center(), Offset::uniform(LOADER_OUTER as _)) + .translate(Offset::y(y_offset)); + + rect_fill(area, bg_color); + + let coords = [ + Point::new(area.center().x, area.center().y - RADIUS), + Point::new(area.center().x + DIAGONAL, area.center().y - DIAGONAL), + Point::new(area.center().x + RADIUS, area.center().y), + Point::new(area.center().x + DIAGONAL, area.center().y + DIAGONAL), + Point::new(area.center().x, area.center().y + RADIUS), + Point::new(area.center().x - DIAGONAL, area.center().y + DIAGONAL), + Point::new(area.center().x - RADIUS, area.center().y), + Point::new(area.center().x - DIAGONAL, area.center().y - DIAGONAL), + ]; + + let big_idx = (progress / 125) as usize % 8; + + for (i, c) in coords.iter().enumerate() { + if i == big_idx { + star_large(*c, fg_color, bg_color); + } else if (big_idx + 1) % 8 == i || (big_idx - 1) % 8 == i { + star_medium(*c, fg_color, bg_color); + } else { + star_small(*c, fg_color, bg_color); + } + } + + if let Some((icon, color)) = icon { + icon.draw(area.center(), CENTER, color, bg_color); + } +} diff --git a/core/embed/rust/src/ui/display/mod.rs b/core/embed/rust/src/ui/display/mod.rs index 6fc98fb12b..c146d35447 100644 --- a/core/embed/rust/src/ui/display/mod.rs +++ b/core/embed/rust/src/ui/display/mod.rs @@ -1,5 +1,7 @@ -#[cfg(any(feature = "model_tt", feature = "model_tr"))] pub mod loader; +#[cfg(feature = "jpeg")] +pub mod tjpgd; +pub mod toif; use super::{ constant, @@ -7,25 +9,25 @@ use super::{ }; #[cfg(feature = "dma2d")] use crate::trezorhal::{ - buffers::{get_buffer_16bpp, get_buffer_4bpp, get_text_buffer}, + buffers::{get_buffer_16bpp, get_buffer_4bpp}, dma2d::{ dma2d_setup_4bpp_over_16bpp, dma2d_setup_4bpp_over_4bpp, dma2d_start_blend, dma2d_wait_for_transfer, }, + uzlib::UZLIB_WINDOW_SIZE, }; +#[cfg(not(feature = "dma2d"))] +use crate::ui::geometry::TOP_LEFT; + use crate::{ error::Error, time::Duration, - trezorhal::{ - display, - display::ToifFormat, - qr, time, - uzlib::{UzlibContext, UZLIB_WINDOW_SIZE}, - }, - ui::lerp::Lerp, + trezorhal::{buffers::get_text_buffer, display, qr, time, uzlib::UzlibContext}, + ui::{component::image::Image, lerp::Lerp}, }; use core::slice; +pub use crate::ui::display::toif::Icon; #[cfg(any(feature = "model_tt", feature = "model_tr"))] pub use loader::{loader, loader_indeterminate, LOADER_MAX, LOADER_MIN}; @@ -79,106 +81,6 @@ pub fn rect_fill_rounded(r: Rect, fg_color: Color, bg_color: Color, radius: u8) ); } -/// NOTE: Cannot start at odd x-coordinate. In this case icon is shifted 1px -/// left. -pub fn icon_top_left(top_left: Point, data: &[u8], fg_color: Color, bg_color: Color) { - let (toif_size, toif_data) = toif_info_ensure(data, ToifFormat::GrayScaleEH); - display::icon( - top_left.x, - top_left.y, - toif_size.x, - toif_size.y, - toif_data, - fg_color.into(), - bg_color.into(), - ); -} - -pub fn icon(center: Point, data: &[u8], fg_color: Color, bg_color: Color) { - let (toif_size, toif_data) = toif_info_ensure(data, ToifFormat::GrayScaleEH); - let r = Rect::from_center_and_size(center, toif_size); - display::icon( - r.x0, - r.y0, - r.width(), - r.height(), - toif_data, - fg_color.into(), - bg_color.into(), - ); -} - -pub fn icon_rust(center: Point, data: &[u8], fg_color: Color, bg_color: Color) { - let (toif_size, toif_data) = toif_info_ensure(data, ToifFormat::GrayScaleEH); - let r = Rect::from_center_and_size(center, toif_size); - - let area = r.translate(get_offset()); - let clamped = area.clamp(constant::screen()); - let colortable = get_color_table(fg_color, bg_color); - - set_window(clamped); - - let mut dest = [0_u8; 1]; - - let mut window = [0; UZLIB_WINDOW_SIZE]; - let mut ctx = UzlibContext::new(toif_data, Some(&mut window)); - - for py in area.y0..area.y1 { - for px in area.x0..area.x1 { - let p = Point::new(px, py); - let x = p.x - area.x0; - - if clamped.contains(p) { - if x % 2 == 0 { - unwrap!(ctx.uncompress(&mut dest), "Decompression failed"); - pixeldata(colortable[(dest[0] & 0xF) as usize]); - } else { - pixeldata(colortable[(dest[0] >> 4) as usize]); - } - } else if x % 2 == 0 { - //continue unzipping but dont write to display - unwrap!(ctx.uncompress(&mut dest), "Decompression failed"); - } - } - } - - pixeldata_dirty(); -} - -pub fn image(center: Point, data: &[u8]) { - let (toif_size, toif_data) = toif_info_ensure(data, ToifFormat::FullColorLE); - - let r = Rect::from_center_and_size(center, toif_size); - display::image(r.x0, r.y0, r.width(), r.height(), toif_data); -} - -pub fn toif_info(data: &[u8]) -> Option<(Offset, ToifFormat)> { - if let Ok(info) = display::toif_info(data) { - Some(( - Offset::new( - unwrap!(info.width.try_into()), - unwrap!(info.height.try_into()), - ), - info.format, - )) - } else { - None - } -} - -/// Aborts if the TOIF file does not have the correct grayscale flag, do not use -/// with user-supplied inputs. -fn toif_info_ensure(data: &[u8], format: ToifFormat) -> (Offset, &[u8]) { - let info = unwrap!(display::toif_info(data), "Invalid TOIF data"); - assert_eq!(info.format, format); - let size = Offset::new( - unwrap!(info.width.try_into()), - unwrap!(info.height.try_into()), - ); - let payload = &data[12..]; // Skip TOIF header. - (size, payload) -} - // Used on T1 only. pub fn rect_fill_rounded1(r: Rect, fg_color: Color, bg_color: Color) { display::bar(r.x0, r.y0, r.width(), r.height(), fg_color.into()); @@ -304,7 +206,7 @@ pub fn rect_rounded2_partial( fg_color: Color, bg_color: Color, show_percent: i16, - icon: Option<(&[u8], Color)>, + icon: Option<(Icon, Color)>, ) { const MAX_ICON_SIZE: i16 = 64; @@ -323,17 +225,14 @@ pub fn rect_rounded2_partial( let mut icon_data = [0_u8; ((MAX_ICON_SIZE * MAX_ICON_SIZE) / 2) as usize]; let mut icon_width = 0; - if let Some((icon_bytes, icon_color)) = icon { - let (toif_size, toif_data) = toif_info_ensure(icon_bytes, ToifFormat::GrayScaleEH); - - if toif_size.x <= MAX_ICON_SIZE && toif_size.y <= MAX_ICON_SIZE { - icon_area = Rect::from_center_and_size(center, toif_size); + if let Some((icon, icon_color)) = icon { + let icon_size = icon.toif.size(); + if icon_size.x <= MAX_ICON_SIZE && icon_size.y <= MAX_ICON_SIZE { + icon_area = Rect::from_center_and_size(center, icon_size); icon_area_clamped = icon_area.clamp(constant::screen()); - - let mut ctx = UzlibContext::new(toif_data, None); - unwrap!(ctx.uncompress(&mut icon_data), "Decompression failed"); + icon.toif.uncompress(&mut icon_data); icon_colortable = get_color_table(icon_color, bg_color); - icon_width = toif_size.x; + icon_width = icon.toif.width(); use_icon = true; } } @@ -419,16 +318,29 @@ pub fn rect_rounded2_partial( /// /// `buffer_bpp` determines size of pixel data /// `data_width` sets the width of valid data in the `src_buffer` -fn position_buffer( +pub(crate) fn position_buffer( dest_buffer: &mut [u8], src_buffer: &[u8], buffer_bpp: usize, offset_x: i16, data_width: i16, ) { - let start: usize = (offset_x).clamp(0, constant::WIDTH) as usize; - let end: usize = (offset_x + data_width).clamp(0, constant::WIDTH) as usize; + let data_width_even = if buffer_bpp == 4 && data_width % 2 != 0 { + data_width + 1 + } else { + data_width + }; + + let mut start: usize = (offset_x).clamp(0, constant::WIDTH) as usize; + let mut end: usize = (offset_x + data_width_even).clamp(0, constant::WIDTH) as usize; + + if buffer_bpp == 4 { + start &= !0x01; + end &= !0x01; + } + let width = end - start; + // if the offset is negative, need to skip beginning of uncompressed data let x_sh = if offset_x < 0 { (-offset_x).clamp(0, constant::WIDTH - width as i16) as usize @@ -518,7 +430,7 @@ fn process_buffer( #[cfg(feature = "dma2d")] pub fn text_over_image( bg_area: Option<(Rect, Color)>, - image_data: &[u8], + image: Image, text: &str, font: Font, offset_img: Offset, @@ -533,8 +445,6 @@ pub fn text_over_image( let t2 = unsafe { get_buffer_4bpp(1, true) }; let empty_t = unsafe { get_buffer_4bpp(2, true) }; - let (toif_size, toif_data) = toif_info_ensure(image_data, ToifFormat::FullColorLE); - let r_img; let area; let offset_img_final; @@ -550,10 +460,10 @@ pub fn text_over_image( empty_img.buffer.copy_from_slice(&img1.buffer); area = a; - r_img = Rect::from_top_left_and_size(a.top_left() + offset_img, toif_size); + r_img = Rect::from_top_left_and_size(a.top_left() + offset_img, image.toif.size()); offset_img_final = offset_img; } else { - area = Rect::from_top_left_and_size(offset_img.into(), toif_size); + area = Rect::from_top_left_and_size(offset_img.into(), image.toif.size()); r_img = area; offset_img_final = Offset::zero(); } @@ -579,7 +489,7 @@ pub fn text_over_image( set_window(clamped); let mut window = [0; UZLIB_WINDOW_SIZE]; - let mut ctx = UzlibContext::new(toif_data, Some(&mut window)); + let mut ctx = image.toif.decompression_context(Some(&mut window)); dma2d_setup_4bpp_over_16bpp(text_color.into()); @@ -647,8 +557,8 @@ pub fn text_over_image( #[cfg(feature = "dma2d")] pub fn icon_over_icon( bg_area: Option, - bg: (&[u8], Offset, Color), - fg: (&[u8], Offset, Color), + bg: (Icon, Offset, Color), + fg: (Icon, Offset, Color), bg_color: Color, ) { let bg1 = unsafe { get_buffer_16bpp(0, true) }; @@ -658,41 +568,40 @@ pub fn icon_over_icon( let fg2 = unsafe { get_buffer_4bpp(1, true) }; let empty2 = unsafe { get_buffer_4bpp(2, true) }; - let (data_bg, offset_bg, color_icon_bg) = bg; - let (data_fg, offset_fg, color_icon_fg) = fg; + let (icon_bg, offset_bg, color_icon_bg) = bg; + let (icon_fg, offset_fg, color_icon_fg) = fg; - let (toif_bg_size, toif_bg_data) = toif_info_ensure(data_bg, ToifFormat::GrayScaleEH); - assert!(toif_bg_size.x <= constant::WIDTH); - assert_eq!(toif_bg_size.x % 2, 0); + assert!(icon_bg.toif.width() <= constant::WIDTH); + assert_eq!(icon_bg.toif.width() % 2, 0); - let (toif_fg_size, toif_fg_data) = toif_info_ensure(data_fg, ToifFormat::GrayScaleEH); - assert!(toif_bg_size.x <= constant::WIDTH); - assert_eq!(toif_bg_size.x % 2, 0); + assert!(icon_fg.toif.width() <= constant::WIDTH); + assert_eq!(icon_fg.toif.width() % 2, 0); let area; let r_bg; let final_offset_bg; if let Some(a) = bg_area { area = a; - r_bg = Rect::from_top_left_and_size(a.top_left() + offset_bg, toif_bg_size); + r_bg = Rect::from_top_left_and_size(a.top_left() + offset_bg, icon_bg.toif.size()); final_offset_bg = offset_bg; } else { - r_bg = Rect::from_top_left_and_size(Point::new(offset_bg.x, offset_bg.y), toif_bg_size); + r_bg = + Rect::from_top_left_and_size(Point::new(offset_bg.x, offset_bg.y), icon_bg.toif.size()); area = r_bg; final_offset_bg = Offset::zero(); } - let r_fg = Rect::from_top_left_and_size(area.top_left() + offset_fg, toif_fg_size); + let r_fg = Rect::from_top_left_and_size(area.top_left() + offset_fg, icon_fg.toif.size()); let clamped = area.clamp(constant::screen()).ensure_even_width(); set_window(clamped); let mut window_bg = [0; UZLIB_WINDOW_SIZE]; - let mut ctx_bg = UzlibContext::new(toif_bg_data, Some(&mut window_bg)); + let mut ctx_bg = UzlibContext::new(icon_bg.toif.zdata(), Some(&mut window_bg)); let mut window_fg = [0; UZLIB_WINDOW_SIZE]; - let mut ctx_fg = UzlibContext::new(toif_fg_data, Some(&mut window_fg)); + let mut ctx_fg = UzlibContext::new(icon_fg.toif.zdata(), Some(&mut window_fg)); dma2d_setup_4bpp_over_4bpp(color_icon_bg.into(), bg_color.into(), color_icon_fg.into()); @@ -751,12 +660,12 @@ pub fn icon_over_icon( #[cfg(not(feature = "dma2d"))] pub fn icon_over_icon( bg_area: Option, - bg: (&[u8], Offset, Color), - fg: (&[u8], Offset, Color), + bg: (Icon, Offset, Color), + fg: (Icon, Offset, Color), bg_color: Color, ) { - let (data_bg, offset_bg, color_icon_bg) = bg; - let (data_fg, offset_fg, color_icon_fg) = fg; + let (icon_bg, offset_bg, color_icon_bg) = bg; + let (icon_fg, offset_fg, color_icon_fg) = fg; let pos_bg = if let Some(area) = bg_area { rect_fill(area, bg_color); @@ -765,8 +674,8 @@ pub fn icon_over_icon( Point::from(offset_bg) }; - icon_top_left(pos_bg, data_bg, color_icon_bg, bg_color); - icon_top_left(pos_bg + offset_fg, data_fg, color_icon_fg, color_icon_bg); + icon_bg.draw(pos_bg, TOP_LEFT, color_icon_bg, bg_color); + icon_fg.draw(pos_bg + offset_fg, TOP_LEFT, color_icon_fg, color_icon_bg); } /// Gets a color of a pixel on `p` coordinates of rounded rectangle with corner @@ -847,6 +756,36 @@ pub fn bar_with_text_and_fill( pixeldata_dirty(); } +pub fn marquee(area: Rect, text: &str, offset: i16, font: Font, fg: Color, bg: Color) { + let buffer = unsafe { get_text_buffer(0, true) }; + + let area = area.translate(get_offset()); + let clamped = area.clamp(constant::screen()); + set_window(clamped); + + display::text_into_buffer(text, font.into(), buffer, offset); + let tbl = get_color_table(fg, bg); + + for y in 0..clamped.height() { + for x in 0..clamped.width() { + let pixel = y * constant::WIDTH + x; + let byte_idx = pixel / 2; + if byte_idx < buffer.buffer.len() as _ { + let data = if pixel % 2 != 0 { + buffer.buffer[byte_idx as usize] >> 4 + } else { + buffer.buffer[byte_idx as usize] & 0xF + }; + pixeldata(tbl[data as usize]); + } else { + pixeldata(bg); + } + } + } + + pixeldata_dirty(); +} + // Used on T1 only. pub fn dotted_line(start: Point, width: i16, color: Color) { for x in (start.x..width).step_by(2) { @@ -916,6 +855,10 @@ pub fn set_window(window: Rect) { ); } +pub fn sync() { + display::sync(); +} + pub fn get_color_table(fg_color: Color, bg_color: Color) -> [Color; 16] { let mut table: [Color; 16] = [Color::from_u16(0); 16]; diff --git a/core/embed/rust/src/ui/display/tjpgd.rs b/core/embed/rust/src/ui/display/tjpgd.rs new file mode 100644 index 0000000000..1edaf664f4 --- /dev/null +++ b/core/embed/rust/src/ui/display/tjpgd.rs @@ -0,0 +1,1554 @@ +/*----------------------------------------------------------------------------/ +/ TJpgDec - Tiny JPEG Decompressor R0.03+trezor (C)ChaN, 2021 +/-----------------------------------------------------------------------------/ +/ The TJpgDec is a generic JPEG decompressor module for tiny embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following terms. +/ +/ Copyright (C) 2021, ChaN, all right reserved. +/ +/ * The TJpgDec module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/ +/-----------------------------------------------------------------------------/ +/ Oct 04, 2011 R0.01 First release. +/ Feb 19, 2012 R0.01a Fixed decompression fails when scan starts with an escape seq. +/ Sep 03, 2012 R0.01b Added JD_TBLCLIP option. +/ Mar 16, 2019 R0.01c Supprted stdint.h. +/ Jul 01, 2020 R0.01d Fixed wrong integer type usage. +/ May 08, 2021 R0.02 Supprted grayscale image. Separated configuration options. +/ Jun 11, 2021 R0.02a Some performance improvement. +/ Jul 01, 2021 R0.03 Added JD_FASTDECODE option. +/ Some performance improvement. +/ Jan 02, 2023 Rust version by Trezor Company, modified to meet our needs. + +Trezor modifications: + - included overflow detection from https://github.com/cmumford/TJpgDec + - removed JD_FASTDECODE=0 option + - removed JD_TBLCLIP option + - allowed interrupted functionality + - tighter integration into Trezor codebase by using our data structures + - removed generic input and output functions, replaced by our specific functionality +/----------------------------------------------------------------------------*/ + +use crate::{ + trezorhal::{ + buffers::{get_jpeg_work_buffer, BufferJpeg}, + display::pixeldata, + }, + ui::{ + constant, + display::set_window, + geometry::{Offset, Point, Rect}, + }, +}; +use core::{ + f64::consts::{FRAC_1_SQRT_2, SQRT_2}, + mem, slice, +}; + +/// Specifies output pixel format. +/// 0: RGB888 (24-bit/pix) +/// 1: RGB565 (16-bit/pix) +/// 2: Grayscale (8-bit/pix) +const JD_FORMAT: u32 = 1; + +/// Switches output descaling feature. +/// 0: Disable +/// 1: Enable +const JD_USE_SCALE: u32 = 1; + +/// Optimization level +/// 0: NOT IMPLEMENTED Basic optimization. Suitable for 8/16-bit MCUs. +/// 1: + 32-bit barrel shifter. Suitable for 32-bit MCUs. +/// 2: + Table conversion for huffman decoding (wants 6 << HUFF_BIT bytes of +/// RAM) +const JD_FASTDECODE: u32 = 2; + +/// Specifies size of stream input buffer +const JD_SZBUF: usize = 512; + +const HUFF_BIT: u32 = 10; +const HUFF_LEN: u32 = 1 << HUFF_BIT; +const HUFF_MASK: u32 = HUFF_LEN - 1; + +const NUM_DEQUANTIZER_TABLES: usize = 4; + +#[derive(PartialEq, Eq)] +pub enum Error { + /// Interrupted by output function, call `JDEC::decomp` to continue. + Interrupted, + /// Device error or wrong termination of input stream. + Input, + /// Insufficient memory pool for the image. + MemoryPool, + /// Insufficient stream input buffer. + MemoryInput, + /// Parameter error. + Parameter, + /// Data format error (may be broken data). + InvalidData, + /// Not supported JPEG standard. + UnsupportedJpeg, +} + +pub struct JDEC<'i, 'p> { + dctr: usize, + dptr: usize, + inbuf: &'p mut [u8], + dbit: u8, + scale: u8, + msx: u8, + msy: u8, + qtid: [u8; 3], + ncomp: u8, + dcv: [i16; 3], + nrst: u16, + rst: u16, + rsc: u16, + width: u16, + height: u16, + huffbits: [[&'p mut [u8]; 2]; 2], + huffcode: [[&'p mut [u16]; 2]; 2], + huffcode_len: [[usize; 2]; 2], + huffdata: [[&'p mut [u8]; 2]; 2], + qttbl: [&'p mut [i32]; 4], + wreg: u32, + marker: u8, + longofs: [[u8; 2]; 2], + hufflut_ac: [&'p mut [u16]; 2], + hufflut_dc: [&'p mut [u8]; 2], + workbuf: &'p mut [i32], + mcubuf: &'p mut [i16], + pool: &'p mut [u8], + input_func: &'i mut dyn JpegInput, +} + +/// Zigzag-order to raster-order conversion table +#[rustfmt::skip] +const ZIG: [u8; 64] = [ + 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63, +]; + +macro_rules! f { + ($num:expr) => {{ + ($num * 8192_f64) as u16 + }}; +} + +/// Input scale factor of Arai algorithm +/// (scaled up 16 bits for fixed point operations) +#[rustfmt::skip] +const IPSF: [u16; 64] = [ + f!(1.00000), f!(1.38704), f!(1.30656), f!(1.17588), f!(1.00000), f!(0.78570), f!(0.54120), f!(0.27590), + f!(1.38704), f!(1.92388), f!(1.81226), f!(1.63099), f!(1.38704), f!(1.08979), f!(0.75066), f!(0.38268), + f!(1.30656), f!(1.81226), f!(1.70711), f!(1.53636), f!(1.30656), f!(1.02656), f!(FRAC_1_SQRT_2), f!(0.36048), + f!(1.17588), f!(1.63099), f!(1.53636), f!(1.38268), f!(1.17588), f!(0.92388), f!(0.63638), f!(0.32442), + f!(1.00000), f!(1.38704), f!(1.30656), f!(1.17588), f!(1.00000), f!(0.78570), f!(0.54120), f!(0.27590), + f!(0.78570), f!(1.08979), f!(1.02656), f!(0.92388), f!(0.78570), f!(0.61732), f!(0.42522), f!(0.21677), + f!(0.54120), f!(0.75066), f!(FRAC_1_SQRT_2), f!(0.63638), f!(0.54120), f!(0.42522), f!(0.29290), f!(0.14932), + f!(0.27590), f!(0.38268), f!(0.36048), f!(0.32442), f!(0.27590), f!(0.21678), f!(0.14932), f!(0.07612), +]; + +impl<'i, 'p> JDEC<'i, 'p> { + /// Allocate a memory block from memory pool + /// `self`: decompressor object reference + /// `ndata` number of `T` items to allocate + fn alloc_slice(&mut self, ndata: usize) -> Result<&'p mut [T], Error> { + let ndata_bytes = ndata * mem::size_of::(); + let ndata_aligned = (ndata_bytes + 3) & !3; + if self.pool.len() < ndata_aligned { + // Err: not enough memory + return Err(Error::MemoryPool); + } + + // SAFETY: + // - Memory is valid because it comes from a valid slice. + // - Memory is initialized because here we consider integers always + // initialized. + // - The slices do not overlap and the original reference is overwritten, + // ensuring that the returned references are exclusive. + unsafe { + let data = slice::from_raw_parts_mut(self.pool.as_mut_ptr() as _, ndata); + let newpool = slice::from_raw_parts_mut( + self.pool.as_mut_ptr().add(ndata_aligned), + self.pool.len() - ndata_aligned, + ); + self.pool = newpool; + Ok(data) + } + } + + fn jpeg_in(&mut self, inbuf_offset: Option, n_data: usize) -> usize { + if let Some(offset) = inbuf_offset { + let inbuf = &mut self.inbuf[offset..offset + n_data]; + self.input_func.read(Some(inbuf), n_data) + } else { + self.input_func.read(None, n_data) + } + } + + /// Create de-quantization and prescaling tables with a DQT segment + /// `self`: decompressor object reference + /// `ndata`: size of input data + fn create_qt_tbl(&mut self, mut ndata: usize) -> Result<(), Error> { + let mut i: u32; + let mut d: u8; + let mut data_idx = 0; + while ndata != 0 { + // Process all tables in the segment + if ndata < 65 { + // Err: table size is unaligned + return Err(Error::InvalidData); + } + ndata -= 65; + + d = self.inbuf[data_idx]; // Get table property + data_idx += 1; + if d & 0xf0 != 0 { + // Err: not 8-bit resolution + return Err(Error::InvalidData); + } + i = (d & 3) as u32; // Get table ID + + // Allocate a memory block for the table + // Register the table + self.qttbl[i as usize] = self.alloc_slice(64)?; + for zi in ZIG { + // Load the table + // Apply scale factor of Arai algorithm to the de-quantizers + self.qttbl[i as usize][zi as usize] = + ((self.inbuf[data_idx] as u32) * IPSF[zi as usize] as u32) as i32; + data_idx += 1; + } + } + Ok(()) + } + + /// Create huffman code tables with a DHT segment + /// `self`: decompressor object reference + /// `ndata`: size of input data + fn create_huffman_tbl(&mut self, mut ndata: usize) -> Result<(), Error> { + let mut j: u32; + let mut b: u32; + let mut cls: usize; + let mut num: usize; + let mut np: usize; + let mut d: u8; + let mut hc: u16; + let mut data_idx = 0; + while ndata != 0 { + // Process all tables in the segment + if ndata < 17 { + // Err: wrong data size + return Err(Error::InvalidData); + } + ndata -= 17; + d = self.inbuf[data_idx]; // Get table number and class + data_idx += 1; + if d & 0xee != 0 { + // Err: invalid class/number + return Err(Error::InvalidData); + } + cls = d as usize >> 4; // class = dc(0)/ac(1) + num = d as usize & 0xf; // table number = 0/1 + // Allocate a memory block for the bit distribution table + self.huffbits[num][cls] = self.alloc_slice(16)?; + + np = 0; + for i in 0..16 { + // Load number of patterns for 1 to 16-bit code + // Get sum of code words for each code + self.huffbits[num][cls][i] = self.inbuf[data_idx]; + np += self.inbuf[data_idx] as usize; + data_idx += 1; + } + // Allocate a memory block for the code word table + self.huffcode[num][cls] = self.alloc_slice(np)?; + self.huffcode_len[num][cls] = np; + + // Re-build huffman code word table + hc = 0; + j = 0; + for i in 0..16 { + b = self.huffbits[num][cls][i] as u32; + while b > 0 { + self.huffcode[num][cls][j as usize] = hc; + hc += 1; + j += 1; + b -= 1; + } + hc <<= 1; + } + if ndata < np { + // Err: wrong data size + return Err(Error::InvalidData); + } + ndata -= np; + + // Allocate a memory block for the decoded data + self.huffdata[num][cls] = self.alloc_slice(np)?; + + // Load decoded data corresponds to each code word + for i in 0..np { + d = self.inbuf[data_idx]; + data_idx += 1; + if cls == 0 && d > 11 { + return Err(Error::InvalidData); + } + self.huffdata[num][cls][i as usize] = d; + } + if JD_FASTDECODE == 2 { + // Create fast huffman decode table + let mut span: u32; + let mut td: u32; + let mut ti: u32; + if cls != 0 { + // LUT for AC elements + self.hufflut_ac[num] = self.alloc_slice(HUFF_LEN as usize)?; + // Default value (0xFFFF: may be long code) + self.hufflut_ac[num].fill(0xffff); + } else { + // LUT for DC elements + self.hufflut_dc[num] = self.alloc_slice(HUFF_LEN as usize)?; + // Default value (0xFF: may be long code) + self.hufflut_dc[num].fill(0xff); + } + let mut i = 0; + + // Create LUT + for b in 0..HUFF_BIT { + j = self.huffbits[num][cls][b as usize] as u32; + while j != 0 { + // Index of input pattern for the code + ti = (self.huffcode[num][cls][i] << (((HUFF_BIT - 1) as u32) - b)) as u32 + & HUFF_MASK; + + if cls != 0 { + // b15..b8: code length, b7..b0: zero run and data length + td = self.huffdata[num][cls][i] as u32 | (b + 1) << 8; + i += 1; + span = 1 << ((HUFF_BIT - 1) - b); + while span != 0 { + span -= 1; + self.hufflut_ac[num][ti as usize] = td as u16; + ti += 1; + } + } else { + // b7..b4: code length, b3..b0: data length + td = self.huffdata[num][cls][i] as u32 | (b + 1) << 4; + i += 1; + span = 1 << ((HUFF_BIT - 1) - b); + while span != 0 { + span -= 1; + self.hufflut_dc[num][ti as usize] = td as u8; + ti += 1; + } + } + j -= 1; + } + } + // Code table offset for long code + self.longofs[num][cls] = i as u8; + } + } + Ok(()) + } + + /// Extract a huffman decoded data from input stream + /// `self`: decompressor object reference + /// `id`: table ID (0:Y, 1:C) + /// `cls`: table class (0:DC, 1:AC) + #[optimize(speed)] + fn huffext(&mut self, id: usize, cls: usize) -> Result { + let mut dc: usize = self.dctr; + let mut dp: usize = self.dptr; + let mut d: u32; + let mut flg: u32 = 0; + let mut nc: u32; + let mut bl: u32; + let mut wbit: u32 = (self.dbit as i32 % 32) as u32; + let mut w: u32 = self.wreg & ((1 << wbit) - 1); + while wbit < 16 { + // Prepare 16 bits into the working register + if self.marker != 0 { + d = 0xff; // Input stream has stalled for a marker. Generate + // stuff bits + } else { + if dc == 0 { + // Buffer empty, re-fill input buffer + dp = 0; // Top of input buffer + dc = self.jpeg_in(Some(0), JD_SZBUF); + if dc == 0 { + // Err: read error or wrong stream termination + return Err(Error::Input); + } + } + d = self.inbuf[dp] as u32; + dp += 1; + + dc -= 1; + if flg != 0 { + // In flag sequence? + flg = 0; // Exit flag sequence + if d != 0 { + // Not an escape of 0xFF but a marker + self.marker = d as u8; + } + d = 0xff; + } else if d == 0xff { + // Is start of flag sequence? + // Enter flag sequence, get trailing byte + flg = 1; + continue; + } + } + // Shift 8 bits in the working register + w = w << 8 | d; + wbit += 8; + } + self.dctr = dc; + self.dptr = dp; + self.wreg = w; + + let mut hb_idx = 0; + let mut hc_idx = 0; + let mut hd_idx = 0; + + if JD_FASTDECODE == 2 { + // Table serch for the short codes + d = w >> (wbit - HUFF_BIT); // Short code as table index + if cls != 0 { + // AC element + d = self.hufflut_ac[id][d as usize] as u32; // Table decode + if d != 0xffff { + // It is done if hit in short code + self.dbit = (wbit - (d >> 8)) as u8; // Snip the code length + return Ok((d & 0xff) as i32); // b7..0: zero run and + // following + // data bits + } + } else { + // DC element + d = self.hufflut_dc[id][d as usize] as u32; // Table decode + if d != 0xff { + // It is done if hit in short code + self.dbit = (wbit - (d >> 4)) as u8; // Snip the code length + return Ok((d & 0xf) as i32); // b3..0: following data bits + } + } + + // Incremental serch for the codes longer than HUFF_BIT + hb_idx = HUFF_BIT; // Bit distribution table + hc_idx = self.longofs[id][cls]; // Code word table + hd_idx = self.longofs[id][cls]; // Data table + bl = (HUFF_BIT + 1) as u32; + } else { + // Incremental search for all codes + bl = 1; + } + + // Incremental search + while bl <= 16 { + nc = self.huffbits[id][cls][hb_idx as usize] as u32; + hb_idx += 1; + if nc != 0 { + d = w >> (wbit - bl); + loop { + // Search the code word in this bit length + if hc_idx as usize >= self.huffcode_len[id][cls] { + return Err(Error::InvalidData); + } + let val = self.huffcode[id][cls][hc_idx as usize]; + if d == val as u32 { + // Matched? + self.dbit = (wbit - bl) as u8; // Snip the huffman code + // Return the decoded data + return Ok(self.huffdata[id][cls][hd_idx as usize] as i32); + } + hc_idx += 1; + hd_idx += 1; + nc -= 1; + if nc == 0 { + break; + } + } + } + bl += 1; + } + + // Err: code not found (may be collapted data) + Err(Error::InvalidData) + } + + /// Extract N bits from input stream + /// `self`: decompressor object reference + /// `nbit`: number of bits to extract (1 to 16) + #[optimize(speed)] + fn bitext(&mut self, nbit: u32) -> Result { + let mut dc: usize = self.dctr; + let mut dp: usize = self.dptr; + let mut d: u32; + let mut flg: u32 = 0; + let mut wbit: u32 = (self.dbit as i32 % 32) as u32; + let mut w: u32 = self.wreg & ((1 << wbit) - 1); + while wbit < nbit { + // Prepare nbit bits into the working register + if self.marker != 0 { + d = 0xff; // Input stream stalled, generate stuff bits + } else { + if dc == 0 { + // Buffer empty, re-fill input buffer + dp = 0; // Top of input buffer + dc = self.jpeg_in(Some(0), JD_SZBUF); + if dc == 0 { + // Err: read error or wrong stream termination + return Err(Error::Input); + } + } + d = self.inbuf[dp] as u32; + dp += 1; + dc -= 1; + if flg != 0 { + // In flag sequence? + flg = 0; // Exit flag sequence + if d != 0 { + // Not an escape of 0xFF but a marker + self.marker = d as u8; + } + d = 0xff; + } else if d == 0xff { + // Is start of flag sequence? + flg = 1; // Enter flag sequence, get trailing byte + continue; + } + } + w = w << 8 | d; + wbit += 8; + } + self.wreg = w; + self.dbit = (wbit - nbit) as u8; + self.dctr = dc; + self.dptr = dp; + + Ok((w >> ((wbit - nbit) % 32)) as i32) + } + + /// Process restart interval + /// `self`: decompressor object reference + /// `rstn`: expected restart sequence number + #[optimize(speed)] + fn restart(&mut self, rstn: u16) -> Result<(), Error> { + let mut dp = self.dptr; + let mut dc: usize = self.dctr; + let mut marker: u16; + if self.marker != 0 { + // Generate a maker if it has been detected + marker = 0xff00 | self.marker as u16; + self.marker = 0; + } else { + marker = 0; + for _ in 0..2 { + // Get a restart marker + if dc == 0 { + // No input data is available, re-fill input buffer + dp = 0; + dc = self.jpeg_in(Some(0), JD_SZBUF); + if dc == 0 { + return Err(Error::Input); + } + } + // Get a byte + let b = self.inbuf[dp] as u16; + marker = marker << 8 | b; + dp += 1; + dc -= 1; + } + self.dptr = dp; + self.dctr = dc; + } + + // Check the marker + if marker & 0xffd8 != 0xffd0 || marker & 7 != rstn & 7 { + // Err: expected RSTn marker was not detected (may be collapted data) + return Err(Error::InvalidData); + } + self.dbit = 0; // Discard stuff bits + // Reset DC offset + self.dcv[0] = 0; + self.dcv[1] = 0; + self.dcv[2] = 0; + Ok(()) + } + + /// Apply Inverse-DCT in Arai Algorithm + /// `src`: input block data (de-quantized and pre-scaled for Arai Algorithm) + /// `dst`: destination to store the block as byte array + #[optimize(speed)] + fn block_idct(src: &mut [i32], dst: &mut [i16]) { + let m13: i32 = (SQRT_2 * 4096_f64) as i32; + let m2: i32 = (1.08239f64 * 4096_f64) as i32; + let m4: i32 = (2.61313f64 * 4096_f64) as i32; + let m5: i32 = (1.84776f64 * 4096_f64) as i32; + let mut v0: i32; + let mut v1: i32; + let mut v2: i32; + let mut v3: i32; + let mut v4: i32; + let mut v5: i32; + let mut v6: i32; + let mut v7: i32; + let mut t10: i32; + let mut t11: i32; + let mut t12: i32; + let mut t13: i32; + + // Process columns + for idx in 0..8 { + // Get even elements + v0 = src[idx]; + v1 = src[idx + 8 * 2]; + v2 = src[idx + 8 * 4]; + v3 = src[idx + 8 * 6]; + + // Process the even elements + t10 = v0 + v2; + t12 = v0 - v2; + t11 = ((v1 - v3) * m13) >> 12; + v3 += v1; + t11 -= v3; + v0 = t10 + v3; + v3 = t10 - v3; + v1 = t11 + t12; + v2 = t12 - t11; + + // Get odd elements + v4 = src[idx + 8 * 7]; + v5 = src[idx + 8]; + v6 = src[idx + 8 * 5]; + v7 = src[idx + 8 * 3]; + + // Process the odd elements + t10 = v5 - v4; + t11 = v5 + v4; + t12 = v6 - v7; + v7 += v6; + v5 = ((t11 - v7) * m13) >> 12; + v7 += t11; + t13 = ((t10 + t12) * m5) >> 12; + v4 = t13 - ((t10 * m2) >> 12); + v6 = t13 - ((t12 * m4) >> 12) - v7; + v5 -= v6; + v4 -= v5; + + // Write-back transformed values + src[idx] = v0 + v7; + src[idx + 8 * 7] = v0 - v7; + src[idx + 8] = v1 + v6; + src[idx + 8 * 6] = v1 - v6; + src[idx + 8 * 2] = v2 + v5; + src[idx + 8 * 5] = v2 - v5; + src[idx + 8 * 3] = v3 + v4; + src[idx + 8 * 4] = v3 - v4; + } + + // Process rows + for idx in (0..64).step_by(8) { + // Get even elements + v0 = src[idx] + (128 << 8); // remove DC offset (-128) here + v1 = src[idx + 2]; + v2 = src[idx + 4]; + v3 = src[idx + 6]; + + // Process the even elements + t10 = v0 + v2; + t12 = v0 - v2; + t11 = ((v1 - v3) * m13) >> 12; + v3 += v1; + t11 -= v3; + v0 = t10 + v3; + v3 = t10 - v3; + v1 = t11 + t12; + v2 = t12 - t11; + + // Get odd elements + v4 = src[idx + 7]; + v5 = src[idx + 1]; + v6 = src[idx + 5]; + v7 = src[idx + 3]; + + // Process the odd elements + t10 = v5 - v4; + t11 = v5 + v4; + t12 = v6 - v7; + v7 += v6; + v5 = ((t11 - v7) * m13) >> 12; + v7 += t11; + t13 = ((t10 + t12) * m5) >> 12; + v4 = t13 - ((t10 * m2) >> 12); + v6 = t13 - ((t12 * m4) >> 12) - v7; + v5 -= v6; + v4 -= v5; + + // Descale the transformed values 8 bits and output a row + dst[idx] = ((v0 + v7) >> 8) as i16; + dst[idx + 7] = ((v0 - v7) >> 8) as i16; + dst[idx + 1] = ((v1 + v6) >> 8) as i16; + dst[idx + 6] = ((v1 - v6) >> 8) as i16; + dst[idx + 2] = ((v2 + v5) >> 8) as i16; + dst[idx + 5] = ((v2 - v5) >> 8) as i16; + dst[idx + 3] = ((v3 + v4) >> 8) as i16; + dst[idx + 4] = ((v3 - v4) >> 8) as i16; + } + } + + /// Load all blocks in an MCU into working buffer + /// `self`: decompressor object reference + #[optimize(speed)] + fn mcu_load(&mut self) -> Result<(), Error> { + let mut d: i32; + let mut e: i32; + let mut blk: u32; + let mut bc: u32; + let mut z: u32; + let mut id: u32; + let mut cmp: u32; + let nby = (self.msx as i32 * self.msy as i32) as u32; // Number of Y blocks (1, 2 or 4) + let mut mcu_buf_idx = 0; // Pointer to the first block of MCU + blk = 0; + while blk < nby + 2 { + // Get nby Y blocks and two C blocks + cmp = if blk < nby { 0 } else { blk - nby + 1 }; // Component number 0:Y, 1:Cb, 2:Cr + if cmp != 0 && self.ncomp as i32 != 3 { + // Clear C blocks if not exist (monochrome image) + for i in 0..64 { + self.mcubuf[mcu_buf_idx + i] = 128; + } + } else { + // Load Y/C blocks from input stream + id = if cmp != 0 { 1 } else { 0 }; // Huffman table ID of this component + + // Extract a DC element from input stream + d = self.huffext(id as usize, 0)?; // Extract a huffman coded data (bit length) + bc = d as u32; + d = self.dcv[cmp as usize] as i32; // DC value of previous block + if bc != 0 { + // If there is any difference from previous block + e = self.bitext(bc)?; // Extract data bits + bc = 1 << (bc - 1); // MSB position + if e as u32 & bc == 0 { + e -= ((bc << 1) - 1) as i32; // Restore negative value + // if + // needed + } + d += e; // Get current value + self.dcv[cmp as usize] = d as i16; // Save current DC value + // for + // next block + } + // De-quantizer table ID for this component + let dqidx = self.qtid[cmp as usize] as usize; + if dqidx >= NUM_DEQUANTIZER_TABLES { + return Err(Error::InvalidData); + } + // De-quantize, apply scale factor of Arai algorithm and descale 8 bits + let dfq = &self.qttbl[dqidx]; + self.workbuf[0] = (d * dfq[0]) >> 8; + + // Extract following 63 AC elements from input stream + self.workbuf[1..64].fill(0); // Initialize all AC elements + z = 1; // Top of the AC elements (in zigzag-order) + loop { + // Extract a huffman coded value (zero runs and bit length) + d = self.huffext(id as usize, 1)?; + if d == 0 { + // EOB? + break; + } + bc = d as u32; + z += bc >> 4; // Skip leading zero run + if z >= 64 { + // Too long zero run + return Err(Error::InvalidData); + } + bc &= 0xf; + if bc != 0 { + // Bit length? + d = self.bitext(bc)?; // Extract data bits + bc = 1 << (bc - 1); // MSB position + if d as u32 & bc == 0 { + // Restore negative value if needed + d -= ((bc << 1) - 1) as i32; + } + let i = ZIG[z as usize] as u32; // Get raster-order index + // De-quantize, apply scale factor of Arai algorithm and descale 8 bits + let dqidx = self.qtid[cmp as usize] as usize; + if dqidx >= NUM_DEQUANTIZER_TABLES { + return Err(Error::InvalidData); + } + let dfq = &self.qttbl[dqidx]; + self.workbuf[i as usize] = (d * dfq[i as usize]) >> 8; + } + z += 1; + if z >= 64 { + break; + } + } + + // C components may not be processed if in grayscale output + if JD_FORMAT != 2 || cmp == 0 { + // If no AC element or scale ratio is 1/8, IDCT can be omitted and the block is + // filled with DC value + if z == 1 || JD_USE_SCALE != 0 && self.scale == 3 { + d = (self.workbuf[0] / 256 + 128) as i32; + if JD_FASTDECODE >= 1 { + for i in 0..64 { + self.mcubuf[mcu_buf_idx + i] = d as i16; + } + } else { + self.mcubuf[..64].fill(d as i16); + } + } else { + // Apply IDCT and store the block to the MCU buffer + Self::block_idct(self.workbuf, &mut self.mcubuf[mcu_buf_idx..]); + } + } + } + mcu_buf_idx += 64; // Next block + blk += 1; + } + Ok(()) // All blocks have been loaded successfully + } + + /// Output an MCU: Convert YCrCb to RGB and output it in RGB form + /// `self`: decompressor object reference + /// `x`: MCU location in the image + /// `y`: MCU location in the image + #[optimize(speed)] + fn mcu_output( + &mut self, + mut x: u32, + mut y: u32, + output_func: &mut dyn JpegOutput, + ) -> Result<(), Error> { + // Adaptive accuracy for both 16-/32-bit systems + let cvacc: i32 = if mem::size_of::() > 2 { 1024 } else { 128 }; + let mut yy: i32; + let mut cb: i32; + let mut cr: i32; + let mut py_idx: usize; + let mut pc_idx: usize; + + // MCU size (pixel) + let mut mx = (self.msx as i32 * 8) as u32; + let my = (self.msy as i32 * 8) as u32; + + // Output rectangular size (it may be clipped at right/bottom end of image) + let mut rx = if (x + mx) <= self.width as u32 { + mx + } else { + self.width as u32 - x + }; + let mut ry = if (y + my) <= self.height as u32 { + my + } else { + self.height as u32 - y + }; + if JD_USE_SCALE != 0 { + rx >>= self.scale; + ry >>= self.scale; + if rx == 0 || ry == 0 { + // Skip this MCU if all pixel is to be rounded off + return Ok(()); + } + x >>= self.scale; + y >>= self.scale; + } + let rect = Rect::from_top_left_and_size( + Point::new(x as i16, y as i16), + Offset::new(rx as i16, ry as i16), + ); + + // SAFETY: Aligning to u8 slice is safe, because the original slice is aligned + // to 32 bits, therefore there are also no residuals (prefix/suffix). + // The data in the slices are integers, so these are valid for both i32 + // and u8. + let (_, workbuf, _) = unsafe { self.workbuf.align_to_mut::() }; + + let mut pix_idx: usize = 0; + let mut op_idx: usize; + + if JD_USE_SCALE == 0 || self.scale != 3 { + // Not for 1/8 scaling + if JD_FORMAT != 2 { + // RGB output (build an RGB MCU from Y/C component) + for iy in 0..my { + py_idx = 0; + pc_idx = 0; + if my == 16 { + // Double block height? + pc_idx += (64 * 4) + ((iy as usize >> 1) * 8); + if iy >= 8 { + py_idx += 64; + } + } else { + // Single block height + pc_idx += (mx * 8 + iy * 8) as usize; + } + py_idx += (iy * 8) as usize; + for ix in 0..mx { + cb = self.mcubuf[pc_idx] as i32 - 128; // Get Cb/Cr component and remove offset + cr = self.mcubuf[pc_idx + 64] as i32 - 128; + if mx == 16 { + // Double block width? + if ix == 8 { + // Jump to next block if double block height + py_idx += 64 - 8; + } + // Step forward chroma pointer every two pixels + pc_idx += (ix & 1) as usize; + } else { + // Single block width + // Step forward chroma pointer every pixel + pc_idx += 1; + } + // Get Y component + yy = self.mcubuf[py_idx] as i32; + py_idx += 1; + // R + workbuf[pix_idx] = (yy + (1.402f64 * cvacc as f64) as i32 * cr / cvacc) + .clamp(0, 255) as u8; + pix_idx += 1; + // G + workbuf[pix_idx] = (yy + - ((0.344f64 * cvacc as f64) as i32 * cb + + (0.714f64 * cvacc as f64) as i32 * cr) + / cvacc) + .clamp(0, 255) as u8; + pix_idx += 1; + // B + workbuf[pix_idx] = (yy + (1.772f64 * cvacc as f64) as i32 * cb / cvacc) + .clamp(0, 255) as u8; + pix_idx += 1; + } + } + } else { + // Monochrome output (build a grayscale MCU from Y comopnent) + + for iy in 0..my { + py_idx = (iy * 8) as usize; + if my == 16 && iy >= 8 { + // Double block height? + py_idx += 64; + } + for ix in 0..mx { + if mx == 16 && ix == 8 { + // Double block width? + // Jump to next block if double block height + py_idx += 64 - 8; + } + // Get and store a Y value as grayscale + workbuf[pix_idx] = self.mcubuf[py_idx] as u8; + pix_idx += 1; + py_idx += 1; + } + } + } + // Descale the MCU rectangular if needed + if JD_USE_SCALE != 0 && self.scale != 0 { + // Get averaged RGB value of each square corresponds to a pixel + let s = (self.scale * 2) as u32; // Number of shifts for averaging + let w = 1 << self.scale as u32; // Width of square + let a = (mx - w) * (if JD_FORMAT != 2 { 3 } else { 1 }); // Bytes to skip for next line in the square + op_idx = 0; + for iy in (0..my).step_by(w as usize) { + for ix in (0..mx).step_by(w as usize) { + pix_idx = ((iy * mx + ix) * (if JD_FORMAT != 2 { 3 } else { 1 })) as usize; + let mut b = 0; + let mut g = 0; + let mut r = 0; + for _ in 0..w { + // Accumulate RGB value in the square + for _ in 0..w { + // Accumulate R or Y (monochrome output) + r += workbuf[pix_idx] as u32; + pix_idx += 1; + if JD_FORMAT != 2 { + // Accumulate G + g += workbuf[pix_idx] as u32; + pix_idx += 1; + // Accumulate B + b += workbuf[pix_idx] as u32; + pix_idx += 1; + } + } + pix_idx += a as usize; + } + // Put the averaged pixel value + // Put R or Y (monochrome output) + workbuf[op_idx] = (r >> s) as u8; + op_idx += 1; + if JD_FORMAT != 2 { + // RGB output? + // Put G + workbuf[op_idx] = (g >> s) as u8; + op_idx += 1; + // Put B + workbuf[op_idx] = (b >> s) as u8; + op_idx += 1; + } + } + } + } + } else { + // For only 1/8 scaling (left-top pixel in each block are the DC value of the + // block) Build a 1/8 descaled RGB MCU from discrete components + pix_idx = 0; + pc_idx = (mx * my) as usize; + cb = self.mcubuf[pc_idx] as i32 - 128; // Get Cb/Cr component and restore right level + cr = self.mcubuf[pc_idx + 64] as i32 - 128; + + for iy in (0..my).step_by(8) { + py_idx = 0; + if iy == 8 { + py_idx = 64 * 2; + } + for _ in (0..mx).step_by(8) { + // Get Y component + yy = self.mcubuf[py_idx] as i32; + py_idx += 64; + if JD_FORMAT != 2 { + // R + workbuf[pix_idx] = (yy + (1.402f64 * cvacc as f64) as i32 * cr / cvacc) + .clamp(0, 255) as u8; + pix_idx += 1; + // G + workbuf[pix_idx] = (yy + - ((0.344f64 * cvacc as f64) as i32 * cb + + (0.714f64 * cvacc as f64) as i32 * cr) + / cvacc) + .clamp(0, 255) as u8; + //B + pix_idx += 1; + workbuf[pix_idx] = (yy + (1.772f64 * cvacc as f64) as i32 * cb / cvacc) + .clamp(0, 255) as u8; + pix_idx += 1; + } else { + workbuf[pix_idx] = yy as u8; + pix_idx += 1; + } + } + } + } + + // Squeeze up pixel table if a part of MCU is to be truncated + mx >>= self.scale as i32; + if rx < mx { + // Is the MCU spans right edge? + let mut s_0_idx = 0; + let mut d_idx = 0; + for _ in 0..ry { + for _ in 0..rx { + // Copy effective pixels + workbuf[d_idx] = workbuf[s_0_idx]; + s_0_idx += 1; + d_idx += 1; + if JD_FORMAT != 2 { + workbuf[d_idx] = workbuf[s_0_idx]; + s_0_idx += 1; + d_idx += 1; + workbuf[d_idx] = workbuf[s_0_idx]; + s_0_idx += 1; + d_idx += 1; + } + } + // Skip truncated pixels + s_0_idx += ((mx - rx) * (if JD_FORMAT != 2 { 3 } else { 1 })) as usize; + } + } + + // Convert RGB888 to RGB565 if needed + if JD_FORMAT == 1 { + let mut s_1_idx = 0; + let mut d_0_idx = 0; + let mut w_0: u16; + for _ in 0..rx * ry { + // RRRRR----------- + w_0 = ((workbuf[s_1_idx] as i32 & 0xf8) << 8) as u16; + s_1_idx += 1; + // -----GGGGGG----- + w_0 = (w_0 as i32 | (workbuf[s_1_idx] as i32 & 0xfc) << 3) as u16; + s_1_idx += 1; + // -----------BBBBB + w_0 = (w_0 as i32 | workbuf[s_1_idx] as i32 >> 3) as u16; + s_1_idx += 1; + + workbuf[d_0_idx] = (w_0 & 0xFF) as u8; + workbuf[d_0_idx + 1] = (w_0 >> 8) as u8; + d_0_idx += 2; + } + } + + // Output the rectangular + // SAFETY: Aligning to u16 slice is safe, because the original slice is aligned + // to 32 bits, therefore there are also no residuals (prefix/suffix). + // The data in the slices are integers, so these are valid for both i32 + // and u16. + let (_, bitmap, _) = unsafe { self.workbuf.align_to::() }; + let bitmap = &bitmap[..(rect.width() * rect.height()) as usize]; + if output_func.write(self, rect, bitmap) { + Ok(()) + } else { + Err(Error::Interrupted) + } + } + + pub fn mcu_height(&self) -> i16 { + self.msy as i16 * 8 + } + + pub fn width(&self) -> i16 { + self.width as i16 + } + + pub fn height(&self) -> i16 { + self.height as i16 + } + + pub fn set_scale(&mut self, scale: u8) -> Result<(), Error> { + if scale > (if JD_USE_SCALE != 0 { 3 } else { 0 }) { + return Err(Error::Parameter); + } + self.scale = scale; + Ok(()) + } + + /// Analyze the JPEG image and Initialize decompressor object + pub fn new(input_func: &'i mut dyn JpegInput, pool: &'p mut [u8]) -> Result { + let mut jd = JDEC { + dctr: 0, + dptr: 0, + inbuf: &mut [], + dbit: 0, + scale: 0, + msx: 0, + msy: 0, + qtid: [0; 3], + pool, + dcv: [0; 3], + rsc: 0, + width: 0, + height: 0, + huffbits: [[&mut [], &mut []], [&mut [], &mut []]], + huffcode: [[&mut [], &mut []], [&mut [], &mut []]], + huffcode_len: [[0; 2]; 2], + huffdata: [[&mut [], &mut []], [&mut [], &mut []]], + qttbl: [&mut [], &mut [], &mut [], &mut []], + wreg: 0, + marker: 0, + longofs: [[0; 2]; 2], + hufflut_ac: [&mut [], &mut []], + hufflut_dc: [&mut [], &mut []], + workbuf: &mut [], + rst: 0, + ncomp: 0, + nrst: 0, + mcubuf: &mut [], + input_func, + }; + + let mut marker: u16; + let mut ofs: u32; + let mut len: usize; + + // Allocate stream input buffer + jd.inbuf = jd.alloc_slice(JD_SZBUF)?; + + // Find SOI marker + marker = 0; + ofs = marker as u32; + loop { + if jd.jpeg_in(Some(0), 1) != 1 { + // Err: SOI was not detected + return Err(Error::Input); + } + ofs += 1; + marker = ((marker as i32) << 8 | jd.inbuf[0] as i32) as u16; + if marker == 0xffd8 { + break; + } + } + loop { + // Parse JPEG segments + // Get a JPEG marker + if jd.jpeg_in(Some(0), 4) != 4 { + return Err(Error::Input); + } + // Marker + marker = ((jd.inbuf[0] as i32) << 8 | jd.inbuf[1] as i32) as u16; + // Length field + len = ((jd.inbuf[2] as i32) << 8 | jd.inbuf[3] as i32) as usize; + if len <= 2 || marker >> 8 != 0xff { + return Err(Error::InvalidData); + } + len -= 2; // Segment content size + ofs += (4 + len) as u32; // Number of bytes loaded + + match marker & 0xff { + 0xC0 => { + // SOF0 (baseline JPEG) + if len > JD_SZBUF { + return Err(Error::MemoryInput); + } + // Load segment data + if jd.jpeg_in(Some(0), len) != len { + return Err(Error::Input); + } + // Image width in unit of pixel + jd.width = ((jd.inbuf[3] as i32) << 8 | jd.inbuf[4] as i32) as u16; + // Image height in unit of pixel + jd.height = ((jd.inbuf[1] as i32) << 8 | jd.inbuf[2] as i32) as u16; + // Number of color components + jd.ncomp = jd.inbuf[5]; + if jd.ncomp != 3 && jd.ncomp != 1 { + // Err: Supports only Grayscale and Y/Cb/Cr + return Err(Error::UnsupportedJpeg); + } + // Check each image component + for i in 0..jd.ncomp as usize { + // Get sampling factor + let b = jd.inbuf[7 + 3 * i]; + if i == 0 { + // Y component + if b != 0x11 && b != 0x22 && b != 0x21 { + // Check sampling factor + // Err: Supports only 4:4:4, 4:2:0 or 4:2:2 + return Err(Error::UnsupportedJpeg); + } + // Size of MCU [blocks] + jd.msx = (b as i32 >> 4) as u8; + jd.msy = (b as i32 & 15) as u8; + } else if b as i32 != 0x11 { + // Cb/Cr component + // Err: Sampling factor of Cb/Cr must be 1 + return Err(Error::UnsupportedJpeg); + } + // Get dequantizer table ID for this component + jd.qtid[i] = jd.inbuf[8 + 3 * i]; + if jd.qtid[i] as i32 > 3 { + // Err: Invalid ID + return Err(Error::UnsupportedJpeg); + } + } + } + 0xDD => { + // DRI - Define Restart Interval + if len > JD_SZBUF { + return Err(Error::MemoryInput); + } + // Load segment data + if jd.jpeg_in(Some(0), len) != len { + return Err(Error::Input); + } + // Get restart interval (MCUs) + jd.nrst = ((jd.inbuf[0] as i32) << 8 | jd.inbuf[1] as i32) as u16; + } + 0xC4 => { + // DHT - Define Huffman Tables + if len > JD_SZBUF { + return Err(Error::MemoryInput); + } + // Load segment data + if jd.jpeg_in(Some(0), len) != len { + return Err(Error::Input); + } + // Create huffman tables + jd.create_huffman_tbl(len)?; + } + 0xDB => { + // DQT - Define Quantizer Tables + if len > JD_SZBUF { + return Err(Error::MemoryInput); + } + // Load segment data + if jd.jpeg_in(Some(0), len) != len { + return Err(Error::Input); + } + // Create de-quantizer tables + jd.create_qt_tbl(len)?; + } + 0xDA => { + // SOS - Start of Scan + if len > JD_SZBUF { + return Err(Error::MemoryInput); + } + // Load segment data + if jd.jpeg_in(Some(0), len) != len { + return Err(Error::Input); + } + if jd.width == 0 || jd.height == 0 { + // Err: Invalid image size + return Err(Error::InvalidData); + } + if jd.inbuf[0] as i32 != jd.ncomp as i32 { + // Err: Wrong color components + return Err(Error::UnsupportedJpeg); + } + // Check if all tables corresponding to each components have been loaded + for i in 0..jd.ncomp as usize { + // Get huffman table ID + let b = jd.inbuf[2 + 2 * i]; + if b != 0 && b != 0x11 { + // Err: Different table number for DC/AC element + return Err(Error::UnsupportedJpeg); + } + let n = if i != 0 { 1 } else { 0 }; // Component class + + // Check huffman table for this component + if (jd.huffbits[n][0]).is_empty() || (jd.huffbits[n][1]).is_empty() { + // Err: Not loaded + return Err(Error::InvalidData); + } + // Check dequantizer table for this component + if (jd.qttbl[jd.qtid[i] as usize]).is_empty() { + // Err: Not loaded + return Err(Error::InvalidData); + } + } + // Allocate working buffer for MCU and pixel output + let n = jd.msy as i32 * jd.msx as i32; // Number of Y blocks in the MCU + if n == 0 { + // Err: SOF0 has not been loaded + return Err(Error::InvalidData); + } + len = (n * 64 * 3 + 64) as usize; // Allocate buffer for IDCT and RGB output + if len < 256 { + // but at least 256 byte is required for IDCT + len = 256; + } + + jd.workbuf = jd.alloc_slice(len / 4)?; + + // Allocate MCU working buffer + jd.mcubuf = jd.alloc_slice((n as usize + 2) * 64)?; + + // Align stream read offset to JD_SZBUF + ofs %= JD_SZBUF as u32; + if ofs != 0 { + jd.dctr = jd.jpeg_in(Some(ofs as usize), (JD_SZBUF as u32 - ofs) as usize); + } + jd.dptr = (ofs - (if JD_FASTDECODE != 0 { 0 } else { 1 })) as usize; + return Ok(jd); // Initialization succeeded. Ready to + // decompress the JPEG image. + } + // SOF1, SOF2, SOF3, SOF5, SOF6, SOF7, SOF9, SOF10, SOF11, SOF13, SOF14, SOF15, EOI + 0xC1 | 0xC2 | 0xC3 | 0xC5 | 0xC6 | 0xC7 | 0xC9 | 0xCA | 0xCB | 0xCD | 0xCF + | 0xCE | 0xD9 => { + // Unsupported JPEG standard (may be progressive JPEG) + return Err(Error::UnsupportedJpeg); + } + _ => { + // Unknown segment (comment, exif or etc..) + // Skip segment data (null pointer specifies to remove data from the stream) + if jd.jpeg_in(None, len) != len { + return Err(Error::Input); + } + } + } + } + } + + /// Start to decompress the JPEG picture + /// `scale`: output de-scaling factor (0 to 3) + #[optimize(speed)] + pub fn decomp(&mut self, output_func: &mut dyn JpegOutput) -> Result<(), Error> { + let mx = (self.msx as i32 * 8) as u32; // Size of the MCU (pixel) + let my = (self.msy as i32 * 8) as u32; // Size of the MCU (pixel) + let mut y = 0; + while y < self.height as u32 { + // Vertical loop of MCUs + let mut x = 0; + while x < self.width as u32 { + // Horizontal loop of MCUs + if self.nrst != 0 && { + // Process restart interval if enabled + let val = self.rst; + self.rst += 1; + val == self.nrst + } { + let val = self.rsc; + self.rsc += 1; + self.restart(val)?; + self.rst = 1; + } + // Load an MCU (decompress huffman coded stream, dequantize and apply IDCT) + self.mcu_load()?; + // Output the MCU (YCbCr to RGB, scaling and output) + self.mcu_output(x, y, output_func)?; + x += mx; + } + y += my; + } + Ok(()) + } +} + +pub fn jpeg(data: &[u8], pos: Point, scale: u8) { + let pool = unsafe { get_jpeg_work_buffer(0, true).buffer.as_mut_slice() }; + let mut out = PixelDataOutput(pos); + let mut inp = BufferInput(data); + if let Ok(mut jd) = JDEC::new(&mut inp, pool) { + let _ = jd.set_scale(scale); + let _ = jd.decomp(&mut out); + } +} + +pub fn jpeg_info(data: &[u8]) -> Option<(Offset, i16)> { + let pool = unsafe { get_jpeg_work_buffer(0, true).buffer.as_mut_slice() }; + let mut inp = BufferInput(data); + if let Ok(jd) = JDEC::new(&mut inp, pool) { + let mcu_height = jd.mcu_height(); + if mcu_height > 16 { + return None; + } + Some((Offset::new(jd.width(), jd.height()), mcu_height)) + } else { + None + } +} + +pub fn jpeg_test(data: &[u8]) -> bool { + let pool = unsafe { get_jpeg_work_buffer(0, true).buffer.as_mut_slice() }; + let mut inp = BufferInput(data); + if let Ok(mut jd) = JDEC::new(&mut inp, pool) { + if jd.mcu_height() > 16 { + return false; + } + + let mut out = BlackHoleOutput; + let mut res = jd.decomp(&mut out); + while res == Err(Error::Interrupted) { + res = jd.decomp(&mut out); + } + res.is_ok() + } else { + false + } +} + +pub trait JpegInput { + fn read(&mut self, buf: Option<&mut [u8]>, nread: usize) -> usize; +} + +pub struct BufferInput<'i>(pub &'i [u8]); + +impl<'i> JpegInput for BufferInput<'i> { + fn read(&mut self, inbuf: Option<&mut [u8]>, n_data: usize) -> usize { + let len = n_data.min(self.0.len()); + let (toread, newdata) = self.0.split_at(len); + if let Some(inbuf) = inbuf { + (inbuf[..len]).copy_from_slice(toread) + } + self.0 = newdata; + len + } +} + +pub trait JpegOutput { + /// Return `false` to interrupt. + fn write(&mut self, jd: &JDEC, rect: Rect, pixels: &[u16]) -> bool; +} + +pub struct BufferOutput<'o> { + buffer: &'o mut BufferJpeg, + buffer_width: i16, + buffer_height: i16, + current_line: i16, + current_line_pix: i16, +} + +impl<'o> BufferOutput<'o> { + pub fn new(buffer: &'o mut BufferJpeg, buffer_width: i16, buffer_height: i16) -> Self { + Self { + buffer, + buffer_width, + buffer_height, + current_line: 0, + current_line_pix: 0, + } + } + + pub fn buffer(&mut self) -> &mut BufferJpeg { + self.buffer + } +} + +impl<'o> JpegOutput for BufferOutput<'o> { + fn write(&mut self, jd: &JDEC, rect: Rect, bitmap: &[u16]) -> bool { + let w = rect.width(); + let h = rect.height(); + let x = rect.x0; + + if h > self.buffer_height { + // unsupported height, call and let know + return true; + } + + let buffer_len = (self.buffer_width * self.buffer_height) as usize; + + for i in 0..h { + for j in 0..w { + let buffer_pos = ((x + j) + (i * self.buffer_width)) as usize; + if buffer_pos < buffer_len { + self.buffer.buffer[buffer_pos] = bitmap[(i * w + j) as usize]; + } + } + } + + self.current_line_pix += w; + + if self.current_line_pix >= jd.width() { + self.current_line_pix = 0; + self.current_line += jd.mcu_height(); + // finished line, abort and continue later + return false; + } + + true + } +} + +pub struct PixelDataOutput(Point); + +impl JpegOutput for PixelDataOutput { + fn write(&mut self, _jd: &JDEC, rect: Rect, bitmap: &[u16]) -> bool { + let pos = self.0; + let r = rect.translate(pos.into()); + let clamped = r.clamp(constant::screen()); + set_window(clamped); + for py in r.y0..r.y1 { + for px in r.x0..r.x1 { + let p = Point::new(px, py); + if clamped.contains(p) { + let off = p - r.top_left(); + let c = bitmap[(off.y * rect.width() + off.x) as usize]; + pixeldata(c); + } + } + } + true + } +} + +pub struct BlackHoleOutput; + +impl JpegOutput for BlackHoleOutput { + fn write(&mut self, _jd: &JDEC, _rect: Rect, _bitmap: &[u16]) -> bool { + true + } +} diff --git a/core/embed/rust/src/ui/display/toif.rs b/core/embed/rust/src/ui/display/toif.rs new file mode 100644 index 0000000000..2da23f0b9a --- /dev/null +++ b/core/embed/rust/src/ui/display/toif.rs @@ -0,0 +1,129 @@ +use crate::{ + trezorhal::{ + display::ToifFormat, + uzlib::{UzlibContext, UZLIB_WINDOW_SIZE}, + }, + ui::{ + constant, + display::{get_color_table, get_offset, pixeldata, pixeldata_dirty, set_window}, + geometry::{Alignment2D, Offset, Point, Rect}, + }, +}; + +use super::Color; + +const TOIF_HEADER_LENGTH: usize = 12; + +pub fn icon(icon: &Icon, center: Point, fg_color: Color, bg_color: Color) { + let r = Rect::from_center_and_size(center, icon.toif.size()); + let area = r.translate(get_offset()); + let clamped = area.clamp(constant::screen()); + let colortable = get_color_table(fg_color, bg_color); + + set_window(clamped); + + let mut dest = [0_u8; 1]; + + let mut window = [0; UZLIB_WINDOW_SIZE]; + let mut ctx = icon.toif.decompression_context(Some(&mut window)); + + for py in area.y0..area.y1 { + for px in area.x0..area.x1 { + let p = Point::new(px, py); + let x = p.x - area.x0; + + if clamped.contains(p) { + if x % 2 == 0 { + unwrap!(ctx.uncompress(&mut dest), "Decompression failed"); + pixeldata(colortable[(dest[0] & 0xF) as usize]); + } else { + pixeldata(colortable[(dest[0] >> 4) as usize]); + } + } else if x % 2 == 0 { + //continue unzipping but dont write to display + unwrap!(ctx.uncompress(&mut dest), "Decompression failed"); + } + } + } + + pixeldata_dirty(); +} + +/// Holding toif data and allowing it to draw itself. +/// See https://docs.trezor.io/trezor-firmware/misc/toif.html for data format. +#[derive(PartialEq, Eq, Clone, Copy)] +pub struct Toif { + data: &'static [u8], +} + +impl Toif { + pub const fn new(data: &'static [u8]) -> Option { + if data.len() < TOIF_HEADER_LENGTH || data[0] != b'T' || data[1] != b'O' || data[2] != b'I' + { + return None; + } + let zdatalen = u32::from_le_bytes([data[8], data[9], data[10], data[11]]) as usize; + if zdatalen + TOIF_HEADER_LENGTH != data.len() { + return None; + } + Some(Self { data }) + } + + pub const fn format(&self) -> ToifFormat { + match self.data[3] { + b'f' => ToifFormat::FullColorBE, + b'g' => ToifFormat::GrayScaleOH, + b'F' => ToifFormat::FullColorLE, + b'G' => ToifFormat::GrayScaleEH, + _ => panic!(), + } + } + + pub const fn width(&self) -> i16 { + u16::from_le_bytes([self.data[4], self.data[5]]) as i16 + } + + pub const fn height(&self) -> i16 { + u16::from_le_bytes([self.data[6], self.data[7]]) as i16 + } + + pub const fn size(&self) -> Offset { + Offset::new(self.width(), self.height()) + } + + pub fn zdata(&self) -> &'static [u8] { + &self.data[TOIF_HEADER_LENGTH..] + } + + pub fn uncompress(&self, dest: &mut [u8]) { + let mut ctx = self.decompression_context(None); + unwrap!(ctx.uncompress(dest)); + } + + pub fn decompression_context<'a>( + &'a self, + window: Option<&'a mut [u8; UZLIB_WINDOW_SIZE]>, + ) -> UzlibContext { + UzlibContext::new(self.zdata(), window) + } +} + +#[derive(PartialEq, Eq, Clone, Copy)] +pub struct Icon { + pub toif: Toif, +} + +impl Icon { + pub fn new(data: &'static [u8]) -> Self { + let toif = unwrap!(Toif::new(data)); + assert!(toif.format() == ToifFormat::GrayScaleEH); + Self { toif } + } + + /// Display the icon with baseline Point, aligned according to the + /// `alignment` argument. + pub fn draw(&self, baseline: Point, alignment: Alignment2D, fg_color: Color, bg_color: Color) { + let r = Rect::snap(baseline, self.toif.size(), alignment); + icon(self, r.center(), fg_color, bg_color); + } +} diff --git a/core/embed/rust/src/ui/event.rs b/core/embed/rust/src/ui/event.rs index 0700335a86..3c273f9969 100644 --- a/core/embed/rust/src/ui/event.rs +++ b/core/embed/rust/src/ui/event.rs @@ -53,3 +53,9 @@ impl TouchEvent { Ok(result) } } + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum USBEvent { + /// USB host has connected/disconnected. + Connected(bool), +} diff --git a/core/embed/rust/src/ui/geometry.rs b/core/embed/rust/src/ui/geometry.rs index 09703cb76a..3397069216 100644 --- a/core/embed/rust/src/ui/geometry.rs +++ b/core/embed/rust/src/ui/geometry.rs @@ -77,13 +77,13 @@ impl Offset { /// With `self` representing a rectangle size, returns top-left corner of /// the rectangle such that it is aligned relative to the `point`. - pub const fn snap(self, point: Point, x: Alignment, y: Alignment) -> Point { - let x_off = match x { + pub const fn snap(self, point: Point, alignment: Alignment2D) -> Point { + let x_off = match alignment.0 { Alignment::Start => 0, Alignment::Center => self.x / 2, Alignment::End => self.x, }; - let y_off = match y { + let y_off = match alignment.1 { Alignment::Start => 0, Alignment::Center => self.y / 2, Alignment::End => self.y, @@ -224,6 +224,12 @@ impl Rect { Self::new(Point::zero(), Point::zero()) } + /// Returns a rectangle of `size` such that `point` is on position specified + /// by `alignment`. + pub const fn snap(point: Point, size: Offset, alignment: Alignment2D) -> Rect { + Self::from_top_left_and_size(size.snap(point, alignment), size) + } + pub const fn from_top_left_and_size(p0: Point, size: Offset) -> Self { Self { x0: p0.x, @@ -234,12 +240,12 @@ impl Rect { } pub const fn from_center_and_size(p: Point, size: Offset) -> Self { - Self { - x0: p.x - size.x / 2, - y0: p.y - size.y / 2, - x1: p.x + size.x / 2, - y1: p.y + size.y / 2, - } + let x0 = p.x - size.x / 2; + let y0 = p.y - size.y / 2; + let x1 = x0 + size.x; + let y1 = y0 + size.y; + + Self { x0, y0, x1, y1 } } pub const fn with_top_left(self, p0: Point) -> Self { @@ -290,6 +296,10 @@ impl Rect { self.top_left().center(self.bottom_right()) } + pub const fn top_center(&self) -> Point { + self.top_left().center(self.top_right()) + } + pub const fn bottom_center(&self) -> Point { self.bottom_left().center(self.bottom_right()) } @@ -447,6 +457,15 @@ pub enum Alignment { End, } +pub type Alignment2D = (Alignment, Alignment); + +pub const TOP_LEFT: Alignment2D = (Alignment::Start, Alignment::Start); +pub const TOP_RIGHT: Alignment2D = (Alignment::End, Alignment::Start); +pub const TOP_CENTER: Alignment2D = (Alignment::Center, Alignment::Start); +pub const CENTER: Alignment2D = (Alignment::Center, Alignment::Center); +pub const BOTTOM_LEFT: Alignment2D = (Alignment::Start, Alignment::End); +pub const BOTTOM_RIGHT: Alignment2D = (Alignment::End, Alignment::End); + #[derive(Copy, Clone, PartialEq, Eq)] pub enum Axis { Horizontal, diff --git a/core/embed/rust/src/ui/layout/obj.rs b/core/embed/rust/src/ui/layout/obj.rs index e361141150..4f6c82462d 100644 --- a/core/embed/rust/src/ui/layout/obj.rs +++ b/core/embed/rust/src/ui/layout/obj.rs @@ -6,6 +6,7 @@ use core::{ use crate::{ error::Error, micropython::{ + buffer::StrBuffer, gc::Gc, map::Map, obj::{Obj, ObjBase}, @@ -17,6 +18,7 @@ use crate::{ ui::{ component::{Child, Component, Event, EventCtx, Never, TimerToken}, constant, + display::sync, geometry::Rect, }, }; @@ -25,6 +27,7 @@ use crate::{ use crate::ui::event::ButtonEvent; #[cfg(feature = "touch")] use crate::ui::event::TouchEvent; +use crate::ui::event::USBEvent; /// Conversion trait implemented by components that know how to convert their /// message values into MicroPython `Obj`s. @@ -60,8 +63,9 @@ use maybe_trace::MaybeTrace; pub trait ObjComponent: MaybeTrace { fn obj_place(&mut self, bounds: Rect) -> Rect; fn obj_event(&mut self, ctx: &mut EventCtx, event: Event) -> Result; - fn obj_paint(&mut self); + fn obj_paint(&mut self) -> bool; fn obj_bounds(&self, sink: &mut dyn FnMut(Rect)); + fn obj_skip_paint(&mut self) {} } impl ObjComponent for Child @@ -80,13 +84,19 @@ where } } - fn obj_paint(&mut self) { + fn obj_paint(&mut self) -> bool { + let will_paint = self.will_paint(); self.paint(); + will_paint } fn obj_bounds(&self, sink: &mut dyn FnMut(Rect)) { self.bounds(sink) } + + fn obj_skip_paint(&mut self) { + self.skip_paint() + } } /// `LayoutObj` is a GC-allocated object exported to MicroPython, with type @@ -126,6 +136,13 @@ impl LayoutObj { }) } + pub fn skip_first_paint(&self) { + let mut inner = self.inner.borrow_mut(); + + // SAFETY: `inner.root` is unique because of the `inner.borrow_mut()`. + unsafe { Gc::as_mut(&mut inner.root) }.obj_skip_paint(); + } + /// Timer callback is expected to be a callable object of the following /// form: `def timer(token: int, deadline_in_ms: int)`. fn obj_set_timer_fn(&self, timer_fn: Obj) { @@ -173,8 +190,9 @@ impl LayoutObj { Ok(msg) } - /// Run a paint pass over the component tree. - fn obj_paint_if_requested(&self) { + /// Run a paint pass over the component tree. Returns true if any component + /// actually requested painting since last invocation of the function. + fn obj_paint_if_requested(&self) -> bool { let mut inner = self.inner.borrow_mut(); // Place the root component on the screen in case it was previously requested. @@ -183,8 +201,10 @@ impl LayoutObj { unsafe { Gc::as_mut(&mut inner.root) }.obj_place(constant::screen()); } + sync(); + // SAFETY: `inner.root` is unique because of the `inner.borrow_mut()`. - unsafe { Gc::as_mut(&mut inner.root) }.obj_paint(); + unsafe { Gc::as_mut(&mut inner.root) }.obj_paint() } /// Run a tracing pass over the component tree. Passed `callback` is called @@ -271,6 +291,8 @@ impl LayoutObj { Qstr::MP_QSTR_attach_timer_fn => obj_fn_2!(ui_layout_attach_timer_fn).as_obj(), Qstr::MP_QSTR_touch_event => obj_fn_var!(4, 4, ui_layout_touch_event).as_obj(), Qstr::MP_QSTR_button_event => obj_fn_var!(3, 3, ui_layout_button_event).as_obj(), + Qstr::MP_QSTR_progress_event => obj_fn_var!(3, 3, ui_layout_progress_event).as_obj(), + Qstr::MP_QSTR_usb_event => obj_fn_var!(2, 2, ui_layout_usb_event).as_obj(), Qstr::MP_QSTR_timer => obj_fn_2!(ui_layout_timer).as_obj(), Qstr::MP_QSTR_paint => obj_fn_1!(ui_layout_paint).as_obj(), Qstr::MP_QSTR_request_complete_repaint => obj_fn_1!(ui_layout_request_complete_repaint).as_obj(), @@ -335,8 +357,10 @@ impl TryFrom for Obj { } } -impl From for Obj { - fn from(_: Never) -> Self { +impl TryFrom for Obj { + type Error = Error; + + fn try_from(_: Never) -> Result { unreachable!() } } @@ -394,6 +418,33 @@ extern "C" fn ui_layout_button_event(_n_args: usize, _args: *const Obj) -> Obj { Obj::const_none() } +extern "C" fn ui_layout_progress_event(n_args: usize, args: *const Obj) -> Obj { + let block = |args: &[Obj], _kwargs: &Map| { + if args.len() != 3 { + return Err(Error::TypeError); + } + let this: Gc = args[0].try_into()?; + let value: u16 = args[1].try_into()?; + let description: StrBuffer = args[2].try_into()?; + let msg = this.obj_event(Event::Progress(value, description.as_ref()))?; + Ok(msg) + }; + unsafe { util::try_with_args_and_kwargs(n_args, args, &Map::EMPTY, block) } +} + +extern "C" fn ui_layout_usb_event(n_args: usize, args: *const Obj) -> Obj { + let block = |args: &[Obj], _kwargs: &Map| { + if args.len() != 2 { + return Err(Error::TypeError); + } + let this: Gc = args[0].try_into()?; + let event = USBEvent::Connected(args[1].try_into()?); + let msg = this.obj_event(Event::USB(event))?; + Ok(msg) + }; + unsafe { util::try_with_args_and_kwargs(n_args, args, &Map::EMPTY, block) } +} + extern "C" fn ui_layout_timer(this: Obj, token: Obj) -> Obj { let block = || { let this: Gc = this.try_into()?; @@ -407,8 +458,8 @@ extern "C" fn ui_layout_timer(this: Obj, token: Obj) -> Obj { extern "C" fn ui_layout_paint(this: Obj) -> Obj { let block = || { let this: Gc = this.try_into()?; - this.obj_paint_if_requested(); - Ok(Obj::const_true()) + let painted = this.obj_paint_if_requested().into(); + Ok(painted) }; unsafe { util::try_or_raise(block) } } diff --git a/core/embed/rust/src/ui/layout/util.rs b/core/embed/rust/src/ui/layout/util.rs index c07773954b..e97ea0cd43 100644 --- a/core/embed/rust/src/ui/layout/util.rs +++ b/core/embed/rust/src/ui/layout/util.rs @@ -6,15 +6,28 @@ use crate::{ iter::{Iter, IterBuf}, list::List, obj::Obj, + util::try_or_raise, }, - ui::component::text::{ - paragraphs::{Paragraph, ParagraphSource, ParagraphStrType}, - TextStyle, + ui::{ + component::text::{ + paragraphs::{Paragraph, ParagraphSource, ParagraphStrType}, + TextStyle, + }, + util::set_animation_disabled, }, }; use cstr_core::cstr; use heapless::Vec; +#[cfg(feature = "jpeg")] +use crate::{ + micropython::{ + buffer::get_buffer, + ffi::{mp_obj_new_int, mp_obj_new_tuple}, + }, + ui::display::tjpgd::{jpeg_info, jpeg_test}, +}; + pub fn iter_into_objs(iterable: Obj) -> Result<[Obj; N], Error> { let err = Error::ValueError(cstr!("Invalid iterable length")); let mut vec = Vec::::new(); @@ -228,3 +241,53 @@ impl ParagraphStrType for StrBuffer { self.offset(chars) } } + +pub extern "C" fn upy_disable_animation(disable: Obj) -> Obj { + let block = || { + set_animation_disabled(disable.try_into()?); + Ok(Obj::const_none()) + }; + unsafe { try_or_raise(block) } +} + +#[cfg(feature = "jpeg")] +pub extern "C" fn upy_jpeg_info(data: Obj) -> Obj { + let block = || { + let buffer = unsafe { get_buffer(data) }; + + if let Ok(buffer) = buffer { + let info = jpeg_info(buffer); + + if let Some(info) = info { + let obj = unsafe { + let values = [ + mp_obj_new_int(info.0.x as _), + mp_obj_new_int(info.0.y as _), + mp_obj_new_int(info.1 as _), + ]; + mp_obj_new_tuple(3, values.as_ptr()) + }; + + Ok(obj) + } else { + Err(Error::ValueError(cstr!("Invalid image format."))) + } + } else { + Err(Error::ValueError(cstr!("Buffer error."))) + } + }; + + unsafe { try_or_raise(block) } +} + +#[cfg(feature = "jpeg")] +pub extern "C" fn upy_jpeg_test(data: Obj) -> Obj { + let block = || { + let buffer = + unsafe { get_buffer(data) }.map_err(|_| Error::ValueError(cstr!("Buffer error.")))?; + let result = jpeg_test(buffer); + Ok(result.into()) + }; + + unsafe { try_or_raise(block) } +} diff --git a/core/embed/rust/src/ui/mod.rs b/core/embed/rust/src/ui/mod.rs index 4d792aa7ba..e7aec850f3 100644 --- a/core/embed/rust/src/ui/mod.rs +++ b/core/embed/rust/src/ui/mod.rs @@ -13,8 +13,6 @@ mod util; #[cfg(feature = "micropython")] pub mod layout; -#[cfg(feature = "model_t1")] -pub mod model_t1; #[cfg(feature = "model_tr")] pub mod model_tr; #[cfg(feature = "model_tt")] diff --git a/core/embed/rust/src/ui/model_t1/component/button.rs b/core/embed/rust/src/ui/model_t1/component/button.rs deleted file mode 100644 index cf9ee45d04..0000000000 --- a/core/embed/rust/src/ui/model_t1/component/button.rs +++ /dev/null @@ -1,190 +0,0 @@ -use crate::ui::{ - component::{Component, Event, EventCtx}, - display::{self, Color, Font}, - event::{ButtonEvent, PhysicalButton}, - geometry::{Offset, Point, Rect}, -}; - -use super::theme; - -pub enum ButtonMsg { - Clicked, -} - -#[derive(Copy, Clone)] -pub enum ButtonPos { - Left, - Right, -} - -impl ButtonPos { - fn hit(&self, b: &PhysicalButton) -> bool { - matches!( - (self, b), - (Self::Left, PhysicalButton::Left) | (Self::Right, PhysicalButton::Right) - ) - } -} - -pub struct Button { - area: Rect, - pos: ButtonPos, - baseline: Point, - content: ButtonContent, - styles: ButtonStyleSheet, - state: State, -} - -impl> Button { - pub fn new(pos: ButtonPos, content: ButtonContent, styles: ButtonStyleSheet) -> Self { - Self { - pos, - content, - styles, - baseline: Point::zero(), - area: Rect::zero(), - state: State::Released, - } - } - - pub fn with_text(pos: ButtonPos, text: T, styles: ButtonStyleSheet) -> Self { - Self::new(pos, ButtonContent::Text(text), styles) - } - - pub fn with_icon(pos: ButtonPos, image: &'static [u8], styles: ButtonStyleSheet) -> Self { - Self::new(pos, ButtonContent::Icon(image), styles) - } - - pub fn content(&self) -> &ButtonContent { - &self.content - } - - fn style(&self) -> &ButtonStyle { - match self.state { - State::Released => self.styles.normal, - State::Pressed => self.styles.active, - } - } - - fn set(&mut self, ctx: &mut EventCtx, state: State) { - if self.state != state { - self.state = state; - ctx.request_paint(); - } - } - - fn placement( - area: Rect, - pos: ButtonPos, - content: &ButtonContent, - styles: &ButtonStyleSheet, - ) -> (Rect, Point) { - let border_width = if styles.normal.border_horiz { 2 } else { 0 }; - let content_width = match content { - ButtonContent::Text(text) => styles.normal.font.text_width(text.as_ref()) - 1, - ButtonContent::Icon(_icon) => todo!(), - }; - let button_width = content_width + 2 * border_width; - let area = match pos { - ButtonPos::Left => area.split_left(button_width).0, - ButtonPos::Right => area.split_right(button_width).1, - }; - - let start_of_baseline = area.bottom_left() + Offset::new(border_width, -2); - - (area, start_of_baseline) - } -} - -impl Component for Button -where - T: AsRef, -{ - type Msg = ButtonMsg; - - fn place(&mut self, bounds: Rect) -> Rect { - let (area, baseline) = Self::placement(bounds, self.pos, &self.content, &self.styles); - self.area = area; - self.baseline = baseline; - self.area - } - - fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { - match event { - Event::Button(ButtonEvent::ButtonPressed(which)) if self.pos.hit(&which) => { - self.set(ctx, State::Pressed); - } - Event::Button(ButtonEvent::ButtonReleased(which)) if self.pos.hit(&which) => { - if matches!(self.state, State::Pressed) { - self.set(ctx, State::Released); - return Some(ButtonMsg::Clicked); - } - } - _ => {} - }; - None - } - - fn paint(&mut self) { - let style = self.style(); - - match &self.content { - ButtonContent::Text(text) => { - let background_color = style.text_color.negate(); - if style.border_horiz { - display::rect_fill_rounded1(self.area, background_color, theme::BG); - } else { - display::rect_fill(self.area, background_color) - } - - display::text( - self.baseline, - text.as_ref(), - style.font, - style.text_color, - background_color, - ); - } - ButtonContent::Icon(_image) => { - todo!(); - } - } - } -} - -#[cfg(feature = "ui_debug")] -impl crate::trace::Trace for Button -where - T: AsRef + crate::trace::Trace, -{ - fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Button"); - match &self.content { - ButtonContent::Text(text) => t.field("text", text), - ButtonContent::Icon(_) => t.symbol("icon"), - } - t.close(); - } -} - -#[derive(PartialEq, Eq)] -enum State { - Released, - Pressed, -} - -pub enum ButtonContent { - Text(T), - Icon(&'static [u8]), -} - -pub struct ButtonStyleSheet { - pub normal: &'static ButtonStyle, - pub active: &'static ButtonStyle, -} - -pub struct ButtonStyle { - pub font: Font, - pub text_color: Color, - pub border_horiz: bool, -} diff --git a/core/embed/rust/src/ui/model_t1/component/dialog.rs b/core/embed/rust/src/ui/model_t1/component/dialog.rs deleted file mode 100644 index 39820b3b36..0000000000 --- a/core/embed/rust/src/ui/model_t1/component/dialog.rs +++ /dev/null @@ -1,94 +0,0 @@ -use super::button::{Button, ButtonMsg::Clicked}; -use crate::ui::{ - component::{Child, Component, Event, EventCtx}, - display::Font, - geometry::Rect, -}; - -pub enum DialogMsg { - Content(T), - LeftClicked, - RightClicked, -} - -pub struct Dialog { - content: Child, - left_btn: Option>>, - right_btn: Option>>, -} - -impl Dialog -where - T: Component, - U: AsRef, -{ - pub fn new(content: T, left: Option>, right: Option>) -> Self { - Self { - content: Child::new(content), - left_btn: left.map(Child::new), - right_btn: right.map(Child::new), - } - } - - pub fn inner(&self) -> &T { - self.content.inner() - } -} - -impl Component for Dialog -where - T: Component, - U: AsRef, -{ - type Msg = DialogMsg; - - fn place(&mut self, bounds: Rect) -> Rect { - let button_height = Font::BOLD.line_height() + 2; - let (content_area, button_area) = bounds.split_bottom(button_height); - self.content.place(content_area); - self.left_btn.as_mut().map(|b| b.place(button_area)); - self.right_btn.as_mut().map(|b| b.place(button_area)); - bounds - } - - fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { - if let Some(msg) = self.content.event(ctx, event) { - Some(DialogMsg::Content(msg)) - } else if let Some(Clicked) = self.left_btn.as_mut().and_then(|b| b.event(ctx, event)) { - Some(DialogMsg::LeftClicked) - } else if let Some(Clicked) = self.right_btn.as_mut().and_then(|b| b.event(ctx, event)) { - Some(DialogMsg::RightClicked) - } else { - None - } - } - - fn paint(&mut self) { - self.content.paint(); - if let Some(b) = self.left_btn.as_mut() { - b.paint(); - } - if let Some(b) = self.right_btn.as_mut() { - b.paint(); - } - } -} - -#[cfg(feature = "ui_debug")] -impl crate::trace::Trace for Dialog -where - T: crate::trace::Trace, - U: crate::trace::Trace + AsRef, -{ - fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Dialog"); - t.field("content", &self.content); - if let Some(label) = &self.left_btn { - t.field("left", label); - } - if let Some(label) = &self.right_btn { - t.field("right", label); - } - t.close(); - } -} diff --git a/core/embed/rust/src/ui/model_t1/component/frame.rs b/core/embed/rust/src/ui/model_t1/component/frame.rs deleted file mode 100644 index 47e7a489cf..0000000000 --- a/core/embed/rust/src/ui/model_t1/component/frame.rs +++ /dev/null @@ -1,79 +0,0 @@ -use super::theme; -use crate::ui::{ - component::{Child, Component, Event, EventCtx}, - display::{self, Font}, - geometry::{Insets, Offset, Rect}, -}; - -pub struct Frame { - area: Rect, - title: U, - content: Child, -} - -impl Frame -where - T: Component, - U: AsRef, -{ - pub fn new(title: U, content: T) -> Self { - Self { - title, - area: Rect::zero(), - content: Child::new(content), - } - } - - pub fn inner(&self) -> &T { - self.content.inner() - } -} - -impl Component for Frame -where - T: Component, - U: AsRef, -{ - type Msg = T::Msg; - - fn place(&mut self, bounds: Rect) -> Rect { - const TITLE_SPACE: i16 = 4; - - let (title_area, content_area) = bounds.split_top(Font::BOLD.line_height()); - let content_area = content_area.inset(Insets::top(TITLE_SPACE)); - - self.area = title_area; - self.content.place(content_area); - bounds - } - - fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { - self.content.event(ctx, event) - } - - fn paint(&mut self) { - display::text( - self.area.bottom_left() - Offset::y(2), - self.title.as_ref(), - Font::BOLD, - theme::FG, - theme::BG, - ); - display::dotted_line(self.area.bottom_left(), self.area.width(), theme::FG); - self.content.paint(); - } -} - -#[cfg(feature = "ui_debug")] -impl crate::trace::Trace for Frame -where - T: crate::trace::Trace, - U: crate::trace::Trace + AsRef, -{ - fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Frame"); - t.field("title", &self.title); - t.field("content", &self.content); - t.close(); - } -} diff --git a/core/embed/rust/src/ui/model_t1/component/mod.rs b/core/embed/rust/src/ui/model_t1/component/mod.rs deleted file mode 100644 index dc8c10edb5..0000000000 --- a/core/embed/rust/src/ui/model_t1/component/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -mod button; -mod dialog; -mod frame; -mod page; - -use super::theme; - -pub use button::{Button, ButtonContent, ButtonMsg, ButtonPos, ButtonStyle, ButtonStyleSheet}; -pub use dialog::{Dialog, DialogMsg}; -pub use frame::Frame; -pub use page::ButtonPage; diff --git a/core/embed/rust/src/ui/model_t1/component/page.rs b/core/embed/rust/src/ui/model_t1/component/page.rs deleted file mode 100644 index dadb410937..0000000000 --- a/core/embed/rust/src/ui/model_t1/component/page.rs +++ /dev/null @@ -1,227 +0,0 @@ -use crate::ui::{ - component::{Component, ComponentExt, Event, EventCtx, Never, Pad, PageMsg, Paginate}, - display::{self, Color, Font}, - geometry::{Insets, Offset, Point, Rect}, -}; - -use super::{theme, Button, ButtonMsg, ButtonPos}; - -pub struct ButtonPage { - content: T, - scrollbar: ScrollBar, - pad: Pad, - prev: Button<&'static str>, - next: Button<&'static str>, - cancel: Button<&'static str>, - confirm: Button<&'static str>, -} - -impl ButtonPage -where - T: Paginate, - T: Component, -{ - pub fn new(content: T, background: Color) -> Self { - Self { - content, - scrollbar: ScrollBar::vertical(), - pad: Pad::with_background(background), - prev: Button::with_text(ButtonPos::Left, "BACK", theme::button_cancel()), - next: Button::with_text(ButtonPos::Right, "NEXT", theme::button_default()), - cancel: Button::with_text(ButtonPos::Left, "CANCEL", theme::button_cancel()), - confirm: Button::with_text(ButtonPos::Right, "CONFIRM", theme::button_default()), - } - } - - fn change_page(&mut self, ctx: &mut EventCtx, page: usize) { - // Change the page in the content, clear the background under it and make sure - // it gets completely repainted. - self.content.change_page(page); - self.content.request_complete_repaint(ctx); - self.pad.clear(); - } -} - -impl Component for ButtonPage -where - T: Component, - T: Paginate, -{ - type Msg = PageMsg; - - fn place(&mut self, bounds: Rect) -> Rect { - let button_height = Font::BOLD.line_height() + 2; - let (content_area, button_area) = bounds.split_bottom(button_height); - let (content_area, scrollbar_area) = content_area.split_right(ScrollBar::WIDTH); - let content_area = content_area.inset(Insets::top(1)); - self.pad.place(bounds); - self.content.place(content_area); - let page_count = self.content.page_count(); - self.scrollbar.set_count_and_active_page(page_count, 0); - self.scrollbar.place(scrollbar_area); - self.prev.place(button_area); - self.next.place(button_area); - self.cancel.place(button_area); - self.confirm.place(button_area); - bounds - } - - fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { - ctx.set_page_count(self.scrollbar.page_count); - if self.scrollbar.has_previous_page() { - if let Some(ButtonMsg::Clicked) = self.prev.event(ctx, event) { - // Scroll up. - self.scrollbar.go_to_previous_page(); - self.change_page(ctx, self.scrollbar.active_page); - return None; - } - } else if let Some(ButtonMsg::Clicked) = self.cancel.event(ctx, event) { - return Some(PageMsg::Controls(false)); - } - - if self.scrollbar.has_next_page() { - if let Some(ButtonMsg::Clicked) = self.next.event(ctx, event) { - // Scroll down. - self.scrollbar.go_to_next_page(); - self.change_page(ctx, self.scrollbar.active_page); - return None; - } - } else if let Some(ButtonMsg::Clicked) = self.confirm.event(ctx, event) { - return Some(PageMsg::Controls(true)); - } - - if let Some(msg) = self.content.event(ctx, event) { - return Some(PageMsg::Content(msg)); - } - None - } - - fn paint(&mut self) { - self.pad.paint(); - self.content.paint(); - self.scrollbar.paint(); - if self.scrollbar.has_previous_page() { - self.prev.paint(); - } else { - self.cancel.paint(); - } - if self.scrollbar.has_next_page() { - self.next.paint(); - } else { - self.confirm.paint(); - } - } -} - -#[cfg(feature = "ui_debug")] -impl crate::trace::Trace for ButtonPage -where - T: crate::trace::Trace, -{ - fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("ButtonPage"); - t.field("active_page", &self.scrollbar.active_page); - t.field("page_count", &self.scrollbar.page_count); - t.field("content", &self.content); - t.close(); - } -} - -pub struct ScrollBar { - area: Rect, - page_count: usize, - active_page: usize, -} - -impl ScrollBar { - pub const WIDTH: i16 = 8; - pub const DOT_SIZE: Offset = Offset::new(4, 4); - pub const DOT_INTERVAL: i16 = 6; - - pub fn vertical() -> Self { - Self { - area: Rect::zero(), - page_count: 0, - active_page: 0, - } - } - - pub fn set_count_and_active_page(&mut self, page_count: usize, active_page: usize) { - self.page_count = page_count; - self.active_page = active_page; - } - - pub fn has_next_page(&self) -> bool { - self.active_page < self.page_count - 1 - } - - pub fn has_previous_page(&self) -> bool { - self.active_page > 0 - } - - pub fn go_to_next_page(&mut self) { - self.active_page = self.active_page.saturating_add(1).min(self.page_count - 1); - } - - pub fn go_to_previous_page(&mut self) { - self.active_page = self.active_page.saturating_sub(1); - } - - fn paint_dot(&self, active: bool, top_left: Point) { - let sides = [ - Rect::from_top_left_and_size(top_left + Offset::x(1), Offset::new(2, 1)), - Rect::from_top_left_and_size(top_left + Offset::y(1), Offset::new(1, 2)), - Rect::from_top_left_and_size( - top_left + Offset::new(1, Self::DOT_SIZE.y - 1), - Offset::new(2, 1), - ), - Rect::from_top_left_and_size( - top_left + Offset::new(Self::DOT_SIZE.x - 1, 1), - Offset::new(1, 2), - ), - ]; - for side in sides { - display::rect_fill(side, theme::FG) - } - if active { - display::rect_fill( - Rect::from_top_left_and_size(top_left, Self::DOT_SIZE).inset(Insets::uniform(1)), - theme::FG, - ) - } - } -} - -impl Component for ScrollBar { - type Msg = Never; - - fn place(&mut self, bounds: Rect) -> Rect { - self.area = bounds; - self.area - } - - fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option { - None - } - - fn paint(&mut self) { - let count = self.page_count as i16; - let interval = { - let available_height = self.area.height(); - let naive_height = count * Self::DOT_INTERVAL; - if naive_height > available_height { - available_height / count - } else { - Self::DOT_INTERVAL - } - }; - let mut dot = Point::new( - self.area.center().x - Self::DOT_SIZE.x / 2, - self.area.center().y - (count / 2) * interval, - ); - for i in 0..self.page_count { - self.paint_dot(i == self.active_page, dot); - dot.y += interval - } - } -} diff --git a/core/embed/rust/src/ui/model_t1/constant.rs b/core/embed/rust/src/ui/model_t1/constant.rs deleted file mode 100644 index d4de5b79bf..0000000000 --- a/core/embed/rust/src/ui/model_t1/constant.rs +++ /dev/null @@ -1,14 +0,0 @@ -use crate::ui::geometry::{Offset, Point, Rect}; - -pub const WIDTH: i16 = 128; -pub const HEIGHT: i16 = 64; -pub const LINE_SPACE: i16 = 1; -pub const FONT_BPP: i16 = 1; - -pub const fn size() -> Offset { - Offset::new(WIDTH, HEIGHT) -} - -pub const fn screen() -> Rect { - Rect::from_top_left_and_size(Point::zero(), size()) -} diff --git a/core/embed/rust/src/ui/model_t1/layout.rs b/core/embed/rust/src/ui/model_t1/layout.rs deleted file mode 100644 index d983771aa2..0000000000 --- a/core/embed/rust/src/ui/model_t1/layout.rs +++ /dev/null @@ -1,239 +0,0 @@ -use core::convert::TryInto; - -use crate::{ - error::Error, - micropython::{buffer::StrBuffer, map::Map, module::Module, obj::Obj, qstr::Qstr, util}, - ui::{ - component::{ - base::Component, - paginated::{PageMsg, Paginate}, - text::paragraphs::{Paragraph, Paragraphs}, - FormattedText, - }, - layout::{ - obj::{ComponentMsgObj, LayoutObj}, - result::{CANCELLED, CONFIRMED}, - }, - }, -}; - -use super::{ - component::{Button, ButtonPage, ButtonPos, Frame}, - theme, -}; - -impl ComponentMsgObj for ButtonPage -where - T: Component + Paginate, -{ - fn msg_try_into_obj(&self, msg: Self::Msg) -> Result { - match msg { - PageMsg::Content(_) => Err(Error::TypeError), - PageMsg::Controls(true) => Ok(CONFIRMED.as_obj()), - PageMsg::Controls(false) => Ok(CANCELLED.as_obj()), - PageMsg::GoBack => unreachable!(), - } - } -} - -impl ComponentMsgObj for Frame -where - T: ComponentMsgObj, - U: AsRef, -{ - fn msg_try_into_obj(&self, msg: Self::Msg) -> Result { - self.inner().msg_try_into_obj(msg) - } -} - -extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { - let block = |_args: &[Obj], kwargs: &Map| { - let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; - let action: Option = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?; - let description: Option = - kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?; - let verb: Option = kwargs.get(Qstr::MP_QSTR_verb)?.try_into_option()?; - let verb_cancel: Option = - kwargs.get(Qstr::MP_QSTR_verb_cancel)?.try_into_option()?; - let reverse: bool = kwargs.get(Qstr::MP_QSTR_reverse)?.try_into()?; - - let format = match (&action, &description, reverse) { - (Some(_), Some(_), false) => "{bold}{action}\n\r{normal}{description}", - (Some(_), Some(_), true) => "{normal}{description}\n\r{bold}{action}", - (Some(_), None, _) => "{bold}{action}", - (None, Some(_), _) => "{normal}{description}", - _ => "", - }; - - let _left = verb_cancel - .map(|label| Button::with_text(ButtonPos::Left, label, theme::button_cancel())); - let _right = - verb.map(|label| Button::with_text(ButtonPos::Right, label, theme::button_default())); - - let obj = LayoutObj::new(Frame::new( - title, - ButtonPage::new( - FormattedText::new(theme::TEXT_NORMAL, theme::FORMATTED, format) - .with("action", action.unwrap_or_default()) - .with("description", description.unwrap_or_default()), - theme::BG, - ), - ))?; - Ok(obj.into()) - }; - unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } -} - -extern "C" fn new_confirm_text(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { - let block = |_args: &[Obj], kwargs: &Map| { - let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; - let data: StrBuffer = kwargs.get(Qstr::MP_QSTR_data)?.try_into()?; - let description: Option = - kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?; - - let obj = LayoutObj::new(Frame::new( - title, - ButtonPage::new( - Paragraphs::new([ - Paragraph::new(&theme::TEXT_NORMAL, description.unwrap_or_default()), - Paragraph::new(&theme::TEXT_BOLD, data), - ]), - theme::BG, - ), - ))?; - Ok(obj.into()) - }; - unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } -} - -#[no_mangle] -pub static mp_module_trezorui2: Module = obj_module! { - Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui2.to_obj(), - - /// CONFIRMED: object - Qstr::MP_QSTR_CONFIRMED => CONFIRMED.as_obj(), - - /// CANCELLED: object - Qstr::MP_QSTR_CANCELLED => CANCELLED.as_obj(), - - /// def confirm_action( - /// *, - /// title: str, - /// action: str | None = None, - /// description: str | None = None, - /// verb: str | None = None, - /// verb_cancel: str | None = None, - /// hold: bool | None = None, - /// reverse: bool = False, - /// ) -> object: - /// """Confirm action.""" - Qstr::MP_QSTR_confirm_action => obj_fn_kw!(0, new_confirm_action).as_obj(), - - /// def confirm_text( - /// *, - /// title: str, - /// data: str, - /// description: str | None, - /// ) -> object: - /// """Confirm text.""" - Qstr::MP_QSTR_confirm_text => obj_fn_kw!(0, new_confirm_text).as_obj(), -}; - -#[cfg(test)] -mod tests { - use crate::{ - trace::Trace, - ui::{ - component::Component, - model_t1::{ - component::{Dialog, DialogMsg}, - constant, - }, - }, - }; - - use super::*; - - fn trace(val: &impl Trace) -> String { - let mut t = Vec::new(); - val.trace(&mut t); - String::from_utf8(t).unwrap() - } - - impl ComponentMsgObj for Dialog - where - T: ComponentMsgObj, - U: AsRef, - { - fn msg_try_into_obj(&self, msg: Self::Msg) -> Result { - match msg { - DialogMsg::Content(c) => self.inner().msg_try_into_obj(c), - DialogMsg::LeftClicked => Ok(CANCELLED.as_obj()), - DialogMsg::RightClicked => Ok(CONFIRMED.as_obj()), - } - } - } - - #[test] - fn trace_example_layout() { - let mut layout = Dialog::new( - FormattedText::new( - theme::TEXT_NORMAL, - theme::FORMATTED, - "Testing text layout, with some text, and some more text. And {param}", - ) - .with("param", "parameters!"), - Some(Button::with_text( - ButtonPos::Left, - "Left", - theme::button_cancel(), - )), - Some(Button::with_text( - ButtonPos::Right, - "Right", - theme::button_default(), - )), - ); - layout.place(constant::screen()); - assert_eq!( - trace(&layout), - r#" left: