Merge #6339: chore: release v21.1.1

d627a6ee52 chore: bump version to 21.1.1 (pasta)
5f9700c69a docs: release notes for v21.1.1 (pasta)
1c00726aca Merge #6277: chore: add builder key for kittywhiskers (pasta)
a2bc0f1b1b Merge #6290: chore: update pasta gpg key to reflect new subkeys (pasta)
167608c7c7 Merge #6338: ci: attest results of guix builds (pasta)
6fb4e49ae5 Merge #6197: ci: always build guix, save artifacts (pasta)
c0ca93cf7a Merge #6340: fix: make 6336 compile in v21.1.x branch, using older CHECK_NONFATAL functionality (pasta)
bb96df428f Merge #6336: fix: rpc getblock and getblockstats for blocks with withdrawal transactions (asset unlock) (pasta)
8e70262db4 Merge #6131: feat: make a support of Qt app to show Platform transfer Tx (pasta)
80ed27914e Merge #6328: backport: bitcoin/bitcoin#30131, #23258, #30504 - fix bild for Ubuntu 24.10 + clang (pasta)
bd772fbe8f Merge #6229: fix: `creditOutputs` in AssetLock tx json output should be an array of objects, not debug strings (pasta)
9bf39a93d3 Merge #6222: fix: adjust payee predictions after mn_rr activation, add tests (pasta)
87bebfc246 Merge #6219: fix: correct is_snapshot_cs in VerifyDB (pasta)
a4e6b8a993 Merge #6208: fix: persist coinjoin denoms options from gui over restarts (pasta)

Pull request description:

  ## Issue being fixed or feature implemented
  See commits, backports, release notes, version bump

  ## What was done?

  ## How Has This Been Tested?

  ## Breaking Changes

  ## Checklist:
    _Go over all the following points, and put an `x` in all the boxes that apply._
  - [ ] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

ACKs for top commit:
  knst:
    utACK d627a6ee52
  kwvg:
    ACK d627a6ee52
  UdjinM6:
    utACK d627a6ee52
  ogabrielides:
    utACK d627a6e

Tree-SHA512: cde7e40760e16e9f48da8149c3742d18a34029b057405e4d55b87110da96acbcd19b47280451dd7b5ad1ccfc91fde655452cf5f0f0d1e01a41b4c685337c64b8
This commit is contained in:
pasta 2024-10-22 13:38:53 -05:00
commit 0fcc1561f1
No known key found for this signature in database
GPG Key ID: E2F3D7916E722D38
31 changed files with 563 additions and 147 deletions

View File

@ -1,15 +1,20 @@
name: Guix Build name: Guix Build
permissions:
packages: write
id-token: write
attestations: write
on: on:
pull_request: pull_request_target:
types: [ labeled ] push:
workflow_dispatch:
jobs: jobs:
build: build-image:
runs-on: [ "self-hosted", "linux", "x64", "ubuntu-core" ] runs-on: ubuntu-latest
if: contains(github.event.pull_request.labels.*.name, 'guix-build') outputs:
timeout-minutes: 480 image-tag: ${{ steps.prepare.outputs.image-tag }}
repo-name: ${{ steps.prepare.outputs.repo-name }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -22,37 +27,72 @@ jobs:
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: Commit variables - name: Commit variables
id: dockerfile id: prepare
run: | run: |
echo "hash=$(sha256sum ./dash/contrib/containers/guix/Dockerfile | cut -d ' ' -f1)" >> $GITHUB_OUTPUT echo "hash=$(sha256sum ./dash/contrib/containers/guix/Dockerfile | cut -d ' ' -f1)" >> $GITHUB_OUTPUT
echo "host_user_id=$(id -u)" >> $GITHUB_OUTPUT echo "host_user_id=$(id -u)" >> $GITHUB_OUTPUT
echo "host_group_id=$(id -g)" >> $GITHUB_OUTPUT echo "host_group_id=$(id -g)" >> $GITHUB_OUTPUT
BRANCH_NAME=$(echo "${GITHUB_REF##*/}" | tr '[:upper:]' '[:lower:]')
REPO_NAME=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')
echo "::set-output name=image-tag::${BRANCH_NAME}"
echo "::set-output name=repo-name::${REPO_NAME}"
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker image - name: Build Docker image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v6
with: with:
context: ${{ github.workspace }}/dash context: ${{ github.workspace }}/dash
build-args: | build-args: |
USER_ID=${{ steps.dockerfile.outputs.host_user_id }} USER_ID=${{ steps.prepare.outputs.host_user_id }}
GROUP_ID=${{ steps.dockerfile.outputs.host_group_id }} GROUP_ID=${{ steps.prepare.outputs.host_group_id }}
build-contexts: | build-contexts: |
docker_root=${{ github.workspace }}/dash/contrib/containers/guix docker_root=${{ github.workspace }}/dash/contrib/containers/guix
file: ./dash/contrib/containers/guix/Dockerfile file: ./dash/contrib/containers/guix/Dockerfile
load: true push: true
tags: guix_ubuntu:latest tags: |
cache-from: type=gha ghcr.io/${{ steps.prepare.outputs.repo-name }}/dashcore-guix-builder:${{ steps.prepare.outputs.image-tag }}
cache-to: type=gha,mode=max ghcr.io/${{ steps.prepare.outputs.repo-name }}/dashcore-guix-builder:latest
cache-from: type=registry,ref=ghcr.io/${{ steps.prepare.outputs.repo-name }}/dashcore-guix-builder:latest
cache-to: type=inline,mode=max
- name: Restore Guix cache and depends build:
needs: build-image
# runs-on: [ "self-hosted", "linux", "x64", "ubuntu-core" ]
runs-on: ubuntu-latest
# if: ${{ contains(github.event.pull_request.labels.*.name, 'guix-build') }}
strategy:
matrix:
build_target: [x86_64-linux-gnu, arm-linux-gnueabihf, aarch64-linux-gnu, riscv64-linux-gnu, x86_64-w64-mingw32, x86_64-apple-darwin, arm64-apple-darwin]
timeout-minutes: 480
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
path: dash
fetch-depth: 0
- name: Cache Guix and depends
id: guix-cache-restore id: guix-cache-restore
uses: actions/cache/restore@v3 uses: actions/cache@v3
with: with:
path: | path: |
${{ github.workspace }}/.cache ${{ github.workspace }}/.cache
${{ github.workspace }}/dash/depends/built ${{ github.workspace }}/dash/depends/built
${{ github.workspace }}/dash/depends/sources ${{ github.workspace }}/dash/depends/sources
${{ github.workspace }}/dash/depends/work ${{ github.workspace }}/dash/depends/work
key: ${{ runner.os }}-guix /gnu/store
key: ${{ runner.os }}-guix-${{ matrix.build_target }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-guix-${{ matrix.build_target }}
${{ runner.os }}-guix-
- name: Create .cache folder if missing - name: Create .cache folder if missing
if: steps.guix-cache-restore.outputs.cache-hit != 'true' if: steps.guix-cache-restore.outputs.cache-hit != 'true'
@ -67,8 +107,8 @@ jobs:
-v ${{ github.workspace }}/dash:/src/dash \ -v ${{ github.workspace }}/dash:/src/dash \
-v ${{ github.workspace }}/.cache:/home/ubuntu/.cache \ -v ${{ github.workspace }}/.cache:/home/ubuntu/.cache \
-w /src/dash \ -w /src/dash \
guix_ubuntu:latest && \ ghcr.io/${{ needs.build-image.outputs.repo-name }}/dashcore-guix-builder:${{ needs.build-image.outputs.image-tag }} && \
docker exec guix-daemon bash -c '/usr/local/bin/guix-start' docker exec guix-daemon bash -c 'HOSTS=${{ matrix.build_target }} /usr/local/bin/guix-start'
- name: Ensure build passes - name: Ensure build passes
run: | run: |
@ -77,17 +117,19 @@ jobs:
exit 1 exit 1
fi fi
- name: Save Guix cache and depends
id: guix-cache-save
uses: actions/cache/save@v3
with:
path: |
${{ github.workspace }}/.cache
${{ github.workspace }}/dash/depends/built
${{ github.workspace }}/dash/depends/sources
${{ github.workspace }}/dash/depends/work
key: ${{ steps.guix-cache-restore.outputs.cache-primary-key }}
- name: Compute SHA256 checksums - name: Compute SHA256 checksums
continue-on-error: true # It will complain on depending on only some hosts
run: | run: |
./dash/contrib/containers/guix/scripts/guix-check ${{ github.workspace }}/dash HOSTS=${{ matrix.build_target }} ./dash/contrib/containers/guix/scripts/guix-check ${{ github.workspace }}/dash
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: guix-artifacts-${{ matrix.build_target }}
path: |
${{ github.workspace }}/dash/guix-build*/output/${{ matrix.build_target }}/
- name: Attest build provenance
uses: actions/attest-build-provenance@v1
with:
subject-path: ${{ github.workspace }}/dash/guix-build*/output/${{ matrix.build_target }}/*

View File

@ -2,7 +2,7 @@ AC_PREREQ([2.69])
dnl Don't forget to push a corresponding tag when updating any of _CLIENT_VERSION_* numbers dnl Don't forget to push a corresponding tag when updating any of _CLIENT_VERSION_* numbers
define(_CLIENT_VERSION_MAJOR, 21) define(_CLIENT_VERSION_MAJOR, 21)
define(_CLIENT_VERSION_MINOR, 1) define(_CLIENT_VERSION_MINOR, 1)
define(_CLIENT_VERSION_BUILD, 0) define(_CLIENT_VERSION_BUILD, 1)
define(_CLIENT_VERSION_IS_RELEASE, true) define(_CLIENT_VERSION_IS_RELEASE, true)
define(_COPYRIGHT_YEAR, 2024) define(_COPYRIGHT_YEAR, 2024)
define(_COPYRIGHT_HOLDERS,[The %s developers]) define(_COPYRIGHT_HOLDERS,[The %s developers])

View File

@ -0,0 +1,32 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mDMEZZs7ABYJKwYBBAHaRw8BAQdAxvpS5zLLn9agjKg1bpMyHtKROTC8SLTl3AZm
b4DKXJq0P0tpdHR5d2hpc2tlcnMgVmFuIEdvZ2ggPDYzMTg5NTMxK2t3dmdAdXNl
cnMubm9yZXBseS5naXRodWIuY29tPoiaBBMWCABCAhsDBQkDw7iAAheAAhkBFiEE
lpGHqOdP5AqKSAZ0MM0MBl5cSq0FAmbpq2gFCwkIBwIGFQoJCAsCBRYCAwEAAh4F
AAoJEDDNDAZeXEqt6D4BALOgavknWXzg3zyBI4rzqS2Qq1qrDl0AVohpYQYJrUZ6
AP92LejS8DyeR4NZuUeP4gCxL/0wOydz6LkmEefaTvNiD7RIS2l0dHl3aGlza2Vy
cyBWYW4gR29naCA8NjMxODk1MzEra2l0dHl3aGlza2Vyc0B1c2Vycy5ub3JlcGx5
LmdpdGh1Yi5jb20+iJcEExYIAD8CGwMFCQPDuIACF4AWIQSWkYeo50/kCopIBnQw
zQwGXlxKrQUCZumraQULCQgHAgYVCgkICwIFFgIDAQACHgUACgkQMM0MBl5cSq3B
zAD/T6dYqUtzIuZjIIBXisBMISNTHQxRv1KH3txuN+lCW/UBAIMV6Y41aIqbGnI2
ADm+WYFsnABokj+mT5GZBuqfEYQEtEdLaXR0eXdoaXNrZXJzIFZhbiBHb2doIDw2
MDk4OTc0LWtpdHR5d2hpc2tlcnNAdXNlcnMubm9yZXBseS5naXRsYWIuY29tPoiX
BBMWCAA/AhsDBQkDw7iAAheAFiEElpGHqOdP5AqKSAZ0MM0MBl5cSq0FAmbpq2kF
CwkIBwIGFQoJCAsCBRYCAwEAAh4FAAoJEDDNDAZeXEqt2D0BAIZOVRQgvP6DZeXc
ONNZcFGp3mrbumudjsoCCiDTS/PZAP48LFSFBB8NBcXgjj1edktii9AN3JYyW+yF
60uLMN4NAbQvS2l0dHl3aGlza2VycyBWYW4gR29naCA8a2l0dHl3aGlza2Vyc0Bk
YXNoLm9yZz6IlwQTFgoAPwIbAwUJA8O4gAIXgBYhBJaRh6jnT+QKikgGdDDNDAZe
XEqtBQJm6atpBQsJCAcCBhUKCQgLAgUWAgMBAAIeBQAKCRAwzQwGXlxKrSHfAQCU
Tu3DPWNWj8weotN4NKoShfsMrIEEeKqv1ykLc1K2lwD8CwEBUG69Pl8NFWMElvam
6wu9OWtOKp9xBkFS+CjM8A60NktpdHR5d2hpc2tlcnMgVmFuIEdvZ2ggPGt3dmdA
dXNlcnMubm9yZXBseS5naXRodWIuY29tPoiXBBMWCgA/AhsDBQkDw7iAAheAFiEE
lpGHqOdP5AqKSAZ0MM0MBl5cSq0FAmbpq2kFCwkIBwIGFQoJCAsCBRYCAwEAAh4F
AAoJEDDNDAZeXEqt4YAA/22FrVJGDOeZVYRNLjFL34+YjXEyTO5dACjZ8jV2/uHD
AQDB9osQDYr/lDfuMMSPZhufAryHIWBJp/e8AwHwJ65aALg4BGWbOwASCisGAQQB
l1UBBQEBB0DCbqznf45arlTBDkpS76ineVKFabpOa3vohGKIKJ+5FAMBCAeIfgQY
FggAJhYhBJaRh6jnT+QKikgGdDDNDAZeXEqtBQJlmzsAAhsMBQkDw7iAAAoJEDDN
DAZeXEqtUvEBALBrYJ7jRRCwBMBTG2doiFupibGQh2vN46gKSrXzYSG9AQDIXcCJ
moGvMWiiBz71Wr9JZ7/ZV6rcRE1YXfM06G6gCQ==
=gtD4
-----END PGP PUBLIC KEY BLOCK-----

View File

@ -24,53 +24,143 @@ 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
u+HE6D2rqvc0CA1wAreY5+lD96PbmOAjnA== N+Y0nqxwpCVq6RAJGdjYcT4BCfNTwjdYKqBEPRfK5JP+VZ6RZ6nBfZxUTfzomWWF
=lPi0 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
t8FIucvlOdt4EqKaSmoBQZk0Aj/N5q4=
=vjZr
-----END PGP PUBLIC KEY BLOCK----- -----END PGP PUBLIC KEY BLOCK-----

View File

@ -1,9 +1,8 @@
# Dash Core version v21.1.0 # Dash Core version v21.1.1
This is a new minor version release, bringing important bugfixes. This is a new patch version release, bringing important bugfixes.
This release is **mandatory** for all masternodes. This release is **optional** but recommended for all nodes.
This release is optional but recommended for all other nodes.
Please report bugs using the issue tracker at GitHub: Please report bugs using the issue tracker at GitHub:
@ -34,18 +33,13 @@ reindex or re-sync the whole chain.
# Notable changes # Notable changes
Allow EHF Resigning - Core now categorizes asset unlock transactions as "Platform Transfers" on the Transactions tab in Dash-Qt and in the output of the `gettransaction` RPC (#6131)
------------------- - Persist Coinjoin Denoms options changes made via GUI over restarts (#6208)
- Fix incorrect payment predictions for evonodes in Dash-Qt and in RPC `masternode winners` (#6222)
During implementation, the values for requestID and msgHash for EHF signing were switched. As a result, a masternode - `creditOutputs` entries in various RPCs that output transaction JSON are shown as objects now instead of being shown as strings (#6229)
which participated in an earlier failed attempt to form an EHF message is unable to participate in subsequent - Updated PGP key for builder 'pasta' to reflect new subkeys. You may need to reimport this key to validate signatures. (#6290)
attempts. This is because the LLMQ Signing System requires that the requestID be unique, and that a node will not - Build failures on Ubuntu 24.10 / clang 19.1.1 resolved (#6328)
sign two different msgHash for the same requestID. See the [forum post](https://www.dash.org/forum/index.php?threads/ehf-activation-issues.55146/) - RPC errors in `masternode payments`, `getblock`, `getblockstats` related to Asset Unlock parsing have been fixed (#6336)
explaining it further.
As there is no need to restrict double signing for EHF, we now allow signing of multiple msgHash's for a single EHF
requestID. Once a sufficient number of masternodes upgrade to v21.1, the EHF message will be automatically signed and
mined.
# v21.1.0 Change log # v21.1.0 Change log
@ -55,10 +49,10 @@ See detailed [set of changes][set-of-changes].
Thanks to everyone who directly contributed to this release: Thanks to everyone who directly contributed to this release:
- Kittywhiskers Van Gogh
- Konstantin Akimov - Konstantin Akimov
- PastaPastaPasta - PastaPastaPasta
- UdjinM6 - UdjinM6
- ogabrielides
As well as everyone that submitted issues, reviewed pull requests and helped As well as everyone that submitted issues, reviewed pull requests and helped
debug the release candidates. debug the release candidates.
@ -67,6 +61,7 @@ debug the release candidates.
These release are considered obsolete. Old release notes can be found here: These release are considered obsolete. Old release notes can be found here:
- [v21.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-21.1.0.md) released Aug/8/2024
- [v21.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-21.0.2.md) released Aug/1/2024 - [v21.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-21.0.2.md) released Aug/1/2024
- [v21.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-21.0.0.md) released Jul/25/2024 - [v21.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-21.0.0.md) released Jul/25/2024
- [v20.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.1.1.md) released April/3/2024 - [v20.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.1.1.md) released April/3/2024
@ -118,4 +113,4 @@ These release are considered obsolete. Old release notes can be found here:
- [v0.10.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.10.0.md) released Sep/25/2014 - [v0.10.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.10.0.md) released Sep/25/2014
- [v0.9.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.9.0.md) released Mar/13/2014 - [v0.9.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.9.0.md) released Mar/13/2014
[set-of-changes]: https://github.com/dashpay/dash/compare/v21.0.2...dashpay:v21.1.0 [set-of-changes]: https://github.com/dashpay/dash/compare/v21.1.0...dashpay:v21.1.1

View File

@ -0,0 +1,121 @@
# Dash Core version v21.1.0
This is a new minor version release, bringing important bugfixes.
This release is **mandatory** for all masternodes.
This release is optional but recommended for all other nodes.
Please report bugs using the issue tracker at GitHub:
<https://github.com/dashpay/dash/issues>
# Upgrading and downgrading
## How to Upgrade
If you are running an older version, shut it down. Wait until it has completely
shut down (which might take a few minutes for older versions), then run the
installer (on Windows) or just copy over /Applications/Dash-Qt (on Mac) or
dashd/dash-qt (on Linux).
## Downgrade warning
### Downgrade to a version < v21.0.0
Downgrading to a version older than v21.0.0 may not be supported due to changes
if you are using descriptor wallets.
### Downgrade to a version < v19.2.0
Downgrading to a version older than v19.2.0 is not supported due to changes
in the evodb database. If you need to use an older version, you must either
reindex or re-sync the whole chain.
# Notable changes
Allow EHF Resigning
-------------------
During implementation, the values for requestID and msgHash for EHF signing were switched. As a result, a masternode
which participated in an earlier failed attempt to form an EHF message is unable to participate in subsequent
attempts. This is because the LLMQ Signing System requires that the requestID be unique, and that a node will not
sign two different msgHash for the same requestID. See the [forum post](https://www.dash.org/forum/index.php?threads/ehf-activation-issues.55146/)
explaining it further.
As there is no need to restrict double signing for EHF, we now allow signing of multiple msgHash's for a single EHF
requestID. Once a sufficient number of masternodes upgrade to v21.1, the EHF message will be automatically signed and
mined.
# v21.1.0 Change log
See detailed [set of changes][set-of-changes].
# Credits
Thanks to everyone who directly contributed to this release:
- Konstantin Akimov
- PastaPastaPasta
- UdjinM6
- ogabrielides
As well as everyone that submitted issues, reviewed pull requests and helped
debug the release candidates.
# Older releases
These release are considered obsolete. Old release notes can be found here:
- [v21.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-21.0.2.md) released Aug/1/2024
- [v21.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-21.0.0.md) released Jul/25/2024
- [v20.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.1.1.md) released April/3/2024
- [v20.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.1.0.md) released March/5/2024
- [v20.0.4](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.0.4.md) released Jan/13/2024
- [v20.0.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.0.3.md) released December/26/2023
- [v20.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.0.2.md) released December/06/2023
- [v20.0.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.0.1.md) released November/18/2023
- [v20.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.0.0.md) released November/15/2023
- [v19.3.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-19.3.0.md) released July/31/2023
- [v19.2.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-19.2.0.md) released June/19/2023
- [v19.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-19.1.0.md) released May/22/2023
- [v19.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-19.0.0.md) released Apr/14/2023
- [v18.2.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.2.2.md) released Mar/21/2023
- [v18.2.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.2.1.md) released Jan/17/2023
- [v18.2.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.2.0.md) released Jan/01/2023
- [v18.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.1.1.md) released January/08/2023
- [v18.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.1.0.md) released October/09/2022
- [v18.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.0.2.md) released October/09/2022
- [v18.0.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.0.1.md) released August/17/2022
- [v0.17.0.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.17.0.3.md) released June/07/2021
- [v0.17.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.17.0.2.md) released May/19/2021
- [v0.16.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.16.1.1.md) released November/17/2020
- [v0.16.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.16.1.0.md) released November/14/2020
- [v0.16.0.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.16.0.1.md) released September/30/2020
- [v0.15.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.15.0.0.md) released Febrary/18/2020
- [v0.14.0.5](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.5.md) released December/08/2019
- [v0.14.0.4](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.4.md) released November/22/2019
- [v0.14.0.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.3.md) released August/15/2019
- [v0.14.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.2.md) released July/4/2019
- [v0.14.0.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.1.md) released May/31/2019
- [v0.14.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.md) released May/22/2019
- [v0.13.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.13.3.md) released Apr/04/2019
- [v0.13.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.13.2.md) released Mar/15/2019
- [v0.13.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.13.1.md) released Feb/9/2019
- [v0.13.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.13.0.md) released Jan/14/2019
- [v0.12.3.4](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.4.md) released Dec/14/2018
- [v0.12.3.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.3.md) released Sep/19/2018
- [v0.12.3.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.2.md) released Jul/09/2018
- [v0.12.3.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.1.md) released Jul/03/2018
- [v0.12.2.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.3.md) released Jan/12/2018
- [v0.12.2.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.2.md) released Dec/17/2017
- [v0.12.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.md) released Nov/08/2017
- [v0.12.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.1.md) released Feb/06/2017
- [v0.12.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.0.md) released Aug/15/2015
- [v0.11.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.2.md) released Mar/04/2015
- [v0.11.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.1.md) released Feb/10/2015
- [v0.11.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.0.md) released Jan/15/2015
- [v0.10.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.10.0.md) released Sep/25/2014
- [v0.9.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.9.0.md) released Mar/13/2014
[set-of-changes]: https://github.com/dashpay/dash/compare/v21.0.2...dashpay:v21.1.0

View File

@ -334,7 +334,12 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, bool include_add
} }
if (calculate_fee) { if (calculate_fee) {
const CAmount fee = amt_total_in - amt_total_out; CAmount fee = amt_total_in - amt_total_out;
if (tx.IsPlatformTransfer()) {
auto payload = GetTxPayload<CAssetUnlockPayload>(tx);
CHECK_NONFATAL(payload);
fee = payload->getFee();
}
CHECK_NONFATAL(MoneyRange(fee)); CHECK_NONFATAL(MoneyRange(fee));
entry.pushKV("fee", ValueFromAmount(fee)); entry.pushKV("fee", ValueFromAmount(fee));
} }

View File

@ -22,6 +22,10 @@ namespace llmq {
class CQuorumManager; class CQuorumManager;
} // namespace llmq } // namespace llmq
// Forward declaration from core_io to get rid of circular dependency
UniValue ValueFromAmount(const CAmount amount);
void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex, bool include_addresses);
class CAssetLockPayload class CAssetLockPayload
{ {
public: public:
@ -51,14 +55,18 @@ public:
[[nodiscard]] UniValue ToJson() const [[nodiscard]] UniValue ToJson() const
{ {
UniValue obj; UniValue outputs(UniValue::VARR);
obj.setObject(); for (const CTxOut& credit_output : creditOutputs) {
obj.pushKV("version", int(nVersion)); UniValue out(UniValue::VOBJ);
UniValue outputs; out.pushKV("value", ValueFromAmount(credit_output.nValue));
outputs.setArray(); out.pushKV("valueSat", credit_output.nValue);
for (const CTxOut& out : creditOutputs) { UniValue spk(UniValue::VOBJ);
outputs.push_back(out.ToString()); ScriptPubKeyToUniv(credit_output.scriptPubKey, spk, /* fIncludeHex = */ true, /* include_addresses = */ false);
out.pushKV("scriptPubKey", spk);
outputs.push_back(out);
} }
UniValue obj(UniValue::VOBJ);
obj.pushKV("version", int(nVersion));
obj.pushKV("creditOutputs", outputs); obj.pushKV("creditOutputs", outputs);
return obj; return obj;
} }

View File

@ -215,7 +215,9 @@ std::vector<CDeterministicMNCPtr> CDeterministicMNList::GetProjectedMNPayees(gsl
if (nCount < 0 ) { if (nCount < 0 ) {
return {}; return {};
} }
const auto weighted_count = GetValidWeightedMNsCount(); const bool isMNRewardReallocation = DeploymentActiveAfter(pindexPrev, Params().GetConsensus(),
Consensus::DEPLOYMENT_MN_RR);
const auto weighted_count = isMNRewardReallocation ? GetValidMNsCount() : GetValidWeightedMNsCount();
nCount = std::min(nCount, int(weighted_count)); nCount = std::min(nCount, int(weighted_count));
std::vector<CDeterministicMNCPtr> result; std::vector<CDeterministicMNCPtr> result;
@ -223,7 +225,6 @@ std::vector<CDeterministicMNCPtr> CDeterministicMNList::GetProjectedMNPayees(gsl
int remaining_evo_payments{0}; int remaining_evo_payments{0};
CDeterministicMNCPtr evo_to_be_skipped{nullptr}; CDeterministicMNCPtr evo_to_be_skipped{nullptr};
const bool isMNRewardReallocation{DeploymentActiveAfter(pindexPrev, Params().GetConsensus(), Consensus::DEPLOYMENT_MN_RR)};
if (!isMNRewardReallocation) { if (!isMNRewardReallocation) {
ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) { ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) {
if (dmn->pdmnState->nLastPaidHeight == nHeight) { if (dmn->pdmnState->nLastPaidHeight == nHeight) {
@ -242,7 +243,7 @@ std::vector<CDeterministicMNCPtr> CDeterministicMNList::GetProjectedMNPayees(gsl
ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) { ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) {
if (dmn == evo_to_be_skipped) return; if (dmn == evo_to_be_skipped) return;
for ([[maybe_unused]] auto _ : irange::range(GetMnType(dmn->nType).voting_weight)) { for ([[maybe_unused]] auto _ : irange::range(isMNRewardReallocation ? 1 : GetMnType(dmn->nType).voting_weight)) {
result.emplace_back(dmn); result.emplace_back(dmn);
} }
}); });

View File

@ -328,7 +328,7 @@ bool BaseIndex::BlockUntilSyncedToCurrentChain() const
{ {
// Skip the queue-draining stuff if we know we're caught up with // Skip the queue-draining stuff if we know we're caught up with
// ::ChainActive().Tip(). // m_chain.Tip().
LOCK(cs_main); LOCK(cs_main);
const CBlockIndex* chain_tip = m_chainstate->m_chain.Tip(); const CBlockIndex* chain_tip = m_chainstate->m_chain.Tip();
const CBlockIndex* best_block_index = m_best_block_index.load(); const CBlockIndex* best_block_index = m_best_block_index.load();

View File

@ -408,6 +408,7 @@ struct WalletTx
int64_t time; int64_t time;
std::map<std::string, std::string> value_map; std::map<std::string, std::string> value_map;
bool is_coinbase; bool is_coinbase;
bool is_platform_transfer{false};
bool is_denominate; bool is_denominate;
}; };

View File

@ -554,7 +554,7 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo
if (txHeight != nBestSeenHeight) { if (txHeight != nBestSeenHeight) {
// Ignore side chains and re-orgs; assuming they are random they don't // Ignore side chains and re-orgs; assuming they are random they don't
// affect the estimate. We'll potentially double count transactions in 1-block reorgs. // affect the estimate. We'll potentially double count transactions in 1-block reorgs.
// Ignore txs if BlockPolicyEstimator is not in sync with ::ChainActive().Tip(). // Ignore txs if BlockPolicyEstimator is not in sync with ActiveChain().Tip().
// It will be synced next time a block is processed. // It will be synced next time a block is processed.
return; return;
} }

View File

@ -264,6 +264,11 @@ public:
return nVersion >= SPECIAL_VERSION; return nVersion >= SPECIAL_VERSION;
} }
bool IsPlatformTransfer() const noexcept
{
return IsSpecialTxVersion() && nType == TRANSACTION_ASSET_UNLOCK;
}
bool HasExtraPayloadField() const noexcept bool HasExtraPayloadField() const noexcept
{ {
return IsSpecialTxVersion() && nType != TRANSACTION_NORMAL; return IsSpecialTxVersion() && nType != TRANSACTION_NORMAL;

View File

@ -224,6 +224,8 @@ void OptionsModel::Init(bool resetSettings)
// CoinJoin // CoinJoin
if (!settings.contains("nCoinJoinSessions")) if (!settings.contains("nCoinJoinSessions"))
settings.setValue("nCoinJoinSessions", DEFAULT_COINJOIN_SESSIONS); settings.setValue("nCoinJoinSessions", DEFAULT_COINJOIN_SESSIONS);
if (!gArgs.SoftSetArg("-coinjoinsessions", settings.value("nCoinJoinSessions").toString().toStdString()))
addOverriddenOption("-coinjoinsessions");
if (!settings.contains("nCoinJoinRounds")) if (!settings.contains("nCoinJoinRounds"))
settings.setValue("nCoinJoinRounds", DEFAULT_COINJOIN_ROUNDS); settings.setValue("nCoinJoinRounds", DEFAULT_COINJOIN_ROUNDS);
@ -247,9 +249,13 @@ void OptionsModel::Init(bool resetSettings)
if (!settings.contains("nCoinJoinDenomsGoal")) if (!settings.contains("nCoinJoinDenomsGoal"))
settings.setValue("nCoinJoinDenomsGoal", DEFAULT_COINJOIN_DENOMS_GOAL); settings.setValue("nCoinJoinDenomsGoal", DEFAULT_COINJOIN_DENOMS_GOAL);
if (!gArgs.SoftSetArg("-coinjoindenomsgoal", settings.value("nCoinJoinDenomsGoal").toString().toStdString()))
addOverriddenOption("-coinjoindenomsgoal");
if (!settings.contains("nCoinJoinDenomsHardCap")) if (!settings.contains("nCoinJoinDenomsHardCap"))
settings.setValue("nCoinJoinDenomsHardCap", DEFAULT_COINJOIN_DENOMS_HARDCAP); settings.setValue("nCoinJoinDenomsHardCap", DEFAULT_COINJOIN_DENOMS_HARDCAP);
if (!gArgs.SoftSetArg("-coinjoindenomshardcap", settings.value("nCoinJoinDenomsHardCap").toString().toStdString()))
addOverriddenOption("-coinjoindenomshardcap");
#endif #endif
// Network // Network

View File

@ -93,6 +93,10 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
{ {
strHTML += "<b>" + tr("Source") + ":</b> " + tr("Generated") + "<br>"; strHTML += "<b>" + tr("Source") + ":</b> " + tr("Generated") + "<br>";
} }
else if (wtx.is_platform_transfer)
{
strHTML += "<b>" + tr("Source") + ":</b> " + tr("Platform Transfer") + "<br>";
}
else if (wtx.value_map.count("from") && !wtx.value_map["from"].empty()) else if (wtx.value_map.count("from") && !wtx.value_map["from"].empty())
{ {
// Online transaction // Online transaction

View File

@ -39,7 +39,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(interfaces::Wal
auto node = interfaces::MakeNode(); auto node = interfaces::MakeNode();
auto& coinJoinOptions = node->coinJoinOptions(); auto& coinJoinOptions = node->coinJoinOptions();
if (nNet > 0 || wtx.is_coinbase) if (nNet > 0 || wtx.is_coinbase || wtx.is_platform_transfer)
{ {
// //
// Credit // Credit
@ -74,6 +74,11 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(interfaces::Wal
// Generated // Generated
sub.type = TransactionRecord::Generated; sub.type = TransactionRecord::Generated;
} }
if (wtx.is_platform_transfer)
{
// Withdrawal from platform
sub.type = TransactionRecord::PlatformTransfer;
}
parts.append(sub); parts.append(sub);
} }

View File

@ -96,7 +96,8 @@ public:
CoinJoinCollateralPayment, CoinJoinCollateralPayment,
CoinJoinMakeCollaterals, CoinJoinMakeCollaterals,
CoinJoinCreateDenominations, CoinJoinCreateDenominations,
CoinJoinSend CoinJoinSend,
PlatformTransfer,
}; };
/** Number of confirmation recommended for accepting a transaction */ /** Number of confirmation recommended for accepting a transaction */

View File

@ -431,6 +431,8 @@ QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const
return tr("Payment to yourself"); return tr("Payment to yourself");
case TransactionRecord::Generated: case TransactionRecord::Generated:
return tr("Mined"); return tr("Mined");
case TransactionRecord::PlatformTransfer:
return tr("Platform Transfer");
case TransactionRecord::CoinJoinMixing: case TransactionRecord::CoinJoinMixing:
return tr("%1 Mixing").arg(QString::fromStdString(gCoinJoinName)); return tr("%1 Mixing").arg(QString::fromStdString(gCoinJoinName));
@ -443,9 +445,10 @@ QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const
case TransactionRecord::CoinJoinSend: case TransactionRecord::CoinJoinSend:
return tr("%1 Send").arg(QString::fromStdString(gCoinJoinName)); return tr("%1 Send").arg(QString::fromStdString(gCoinJoinName));
default: case TransactionRecord::Other:
return QString(); break; // use fail-over here
} } // no default case, so the compiler can warn about missing cases
return QString();
} }
QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx) const QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx) const
@ -473,14 +476,20 @@ QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, b
case TransactionRecord::SendToAddress: case TransactionRecord::SendToAddress:
case TransactionRecord::Generated: case TransactionRecord::Generated:
case TransactionRecord::CoinJoinSend: case TransactionRecord::CoinJoinSend:
case TransactionRecord::PlatformTransfer:
return formatAddressLabel(wtx->strAddress, wtx->label, tooltip) + watchAddress; return formatAddressLabel(wtx->strAddress, wtx->label, tooltip) + watchAddress;
case TransactionRecord::SendToOther: case TransactionRecord::SendToOther:
return QString::fromStdString(wtx->strAddress) + watchAddress; return QString::fromStdString(wtx->strAddress) + watchAddress;
case TransactionRecord::SendToSelf: case TransactionRecord::SendToSelf:
return formatAddressLabel(wtx->strAddress, wtx->label, tooltip) + watchAddress; return formatAddressLabel(wtx->strAddress, wtx->label, tooltip) + watchAddress;
default: case TransactionRecord::CoinJoinMixing:
return tr("(n/a)") + watchAddress; case TransactionRecord::CoinJoinCollateralPayment:
} case TransactionRecord::CoinJoinMakeCollaterals:
case TransactionRecord::CoinJoinCreateDenominations:
case TransactionRecord::Other:
break; // use fail-over here
} // no default case, so the compiler can warn about missing cases
return tr("(n/a)") + watchAddress;
} }
QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const
@ -491,6 +500,7 @@ QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const
case TransactionRecord::RecvWithAddress: case TransactionRecord::RecvWithAddress:
case TransactionRecord::SendToAddress: case TransactionRecord::SendToAddress:
case TransactionRecord::Generated: case TransactionRecord::Generated:
case TransactionRecord::PlatformTransfer:
case TransactionRecord::CoinJoinSend: case TransactionRecord::CoinJoinSend:
case TransactionRecord::RecvWithCoinJoin: case TransactionRecord::RecvWithCoinJoin:
{ {
@ -504,9 +514,11 @@ QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const
case TransactionRecord::CoinJoinMakeCollaterals: case TransactionRecord::CoinJoinMakeCollaterals:
case TransactionRecord::CoinJoinCollateralPayment: case TransactionRecord::CoinJoinCollateralPayment:
return GUIUtil::getThemedQColor(GUIUtil::ThemedColor::BAREADDRESS); return GUIUtil::getThemedQColor(GUIUtil::ThemedColor::BAREADDRESS);
default: case TransactionRecord::SendToOther:
case TransactionRecord::RecvFromOther:
case TransactionRecord::Other:
break; break;
} } // no default case, so the compiler can warn about missing cases
return GUIUtil::getThemedQColor(GUIUtil::ThemedColor::DEFAULT); return GUIUtil::getThemedQColor(GUIUtil::ThemedColor::DEFAULT);
} }
@ -530,6 +542,7 @@ QVariant TransactionTableModel::amountColor(const TransactionRecord *rec) const
case TransactionRecord::RecvWithCoinJoin: case TransactionRecord::RecvWithCoinJoin:
case TransactionRecord::RecvWithAddress: case TransactionRecord::RecvWithAddress:
case TransactionRecord::RecvFromOther: case TransactionRecord::RecvFromOther:
case TransactionRecord::PlatformTransfer:
return GUIUtil::getThemedQColor(GUIUtil::ThemedColor::GREEN); return GUIUtil::getThemedQColor(GUIUtil::ThemedColor::GREEN);
case TransactionRecord::CoinJoinSend: case TransactionRecord::CoinJoinSend:
case TransactionRecord::SendToAddress: case TransactionRecord::SendToAddress:

View File

@ -90,6 +90,7 @@ TransactionView::TransactionView(QWidget* parent) :
typeWidget->addItem(tr("%1 Collateral Payment").arg(strCoinJoinName), TransactionFilterProxy::TYPE(TransactionRecord::CoinJoinCollateralPayment)); typeWidget->addItem(tr("%1 Collateral Payment").arg(strCoinJoinName), TransactionFilterProxy::TYPE(TransactionRecord::CoinJoinCollateralPayment));
typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf)); typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf));
typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated)); typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated));
typeWidget->addItem(tr("Platform Transfer"), TransactionFilterProxy::TYPE(TransactionRecord::PlatformTransfer));
typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other)); typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other));
typeWidget->setCurrentIndex(settings.value("transactionType").toInt()); typeWidget->setCurrentIndex(settings.value("transactionType").toInt());

View File

@ -47,6 +47,7 @@
#include <versionbits.h> #include <versionbits.h>
#include <warnings.h> #include <warnings.h>
#include <evo/assetlocktx.h>
#include <evo/cbtx.h> #include <evo/cbtx.h>
#include <evo/evodb.h> #include <evo/evodb.h>
#include <evo/mnhftx.h> #include <evo/mnhftx.h>
@ -2423,6 +2424,13 @@ static RPCHelpMan getblockstats()
} }
CAmount txfee = tx_total_in - tx_total_out; CAmount txfee = tx_total_in - tx_total_out;
if (tx->IsPlatformTransfer()) {
auto payload = GetTxPayload<CAssetUnlockPayload>(*tx);
CHECK_NONFATAL(payload);
txfee = payload->getFee();
}
CHECK_NONFATAL(MoneyRange(txfee)); CHECK_NONFATAL(MoneyRange(txfee));
if (do_medianfee) { if (do_medianfee) {
fee_array.push_back(txfee); fee_array.push_back(txfee);

View File

@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h> #include <chainparams.h>
#include <evo/assetlocktx.h>
#include <evo/chainhelper.h> #include <evo/chainhelper.h>
#include <evo/deterministicmns.h> #include <evo/deterministicmns.h>
#include <governance/classes.h> #include <governance/classes.h>
@ -409,6 +410,13 @@ static RPCHelpMan masternode_payments()
if (tx->IsCoinBase()) { if (tx->IsCoinBase()) {
continue; continue;
} }
if (tx->IsPlatformTransfer()) {
auto payload = GetTxPayload<CAssetUnlockPayload>(*tx);
CHECK_NONFATAL(payload);
nBlockFees += payload->getFee();
continue;
}
CAmount nValueIn{0}; CAmount nValueIn{0};
for (const auto& txin : tx->vin) { for (const auto& txin : tx->vin) {
uint256 blockHashTmp; uint256 blockHashTmp;

View File

@ -524,17 +524,14 @@ public:
* that are guarded by it. * that are guarded by it.
* *
* @par Consistency guarantees * @par Consistency guarantees
*
* By design, it is guaranteed that: * By design, it is guaranteed that:
*
* 1. Locking both `cs_main` and `mempool.cs` will give a view of mempool * 1. Locking both `cs_main` and `mempool.cs` will give a view of mempool
* that is consistent with current chain tip (`::ChainActive()` and * that is consistent with current chain tip (`ActiveChain()` and
* `CoinsTip()`) and is fully populated. Fully populated means that if the * `CoinsTip()`) and is fully populated. Fully populated means that if the
* current active chain is missing transactions that were present in a * current active chain is missing transactions that were present in a
* previously active chain, all the missing transactions will have been * previously active chain, all the missing transactions will have been
* re-added to the mempool and should be present if they meet size and * re-added to the mempool and should be present if they meet size and
* consistency constraints. * consistency constraints.
*
* 2. Locking `mempool.cs` without `cs_main` will give a view of a mempool * 2. Locking `mempool.cs` without `cs_main` will give a view of a mempool
* consistent with some chain that was active since `cs_main` was last * consistent with some chain that was active since `cs_main` was last
* locked, and that is fully populated as described above. It is ok for * locked, and that is fully populated as described above. It is ok for

View File

@ -4678,7 +4678,7 @@ bool CVerifyDB::VerifyDB(
int reportDone = 0; int reportDone = 0;
LogPrintf("[0%%]..."); /* Continued */ LogPrintf("[0%%]..."); /* Continued */
const bool is_snapshot_cs{!chainstate.m_from_snapshot_blockhash}; const bool is_snapshot_cs{chainstate.m_from_snapshot_blockhash};
for (pindex = chainstate.m_chain.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) { for (pindex = chainstate.m_chain.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
const int percentageDone = std::max(1, std::min(99, (int)(((double)(chainstate.m_chain.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100)))); const int percentageDone = std::max(1, std::min(99, (int)(((double)(chainstate.m_chain.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));

View File

@ -104,7 +104,7 @@ static const bool DEFAULT_SYNC_MEMPOOL = true;
/** Default for -stopatheight */ /** Default for -stopatheight */
static const int DEFAULT_STOPATHEIGHT = 0; static const int DEFAULT_STOPATHEIGHT = 0;
/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of ::ChainActive().Tip() will not be pruned. */ /** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of ActiveChain().Tip() will not be pruned. */
static const unsigned int MIN_BLOCKS_TO_KEEP = 288; static const unsigned int MIN_BLOCKS_TO_KEEP = 288;
static const signed int DEFAULT_CHECKBLOCKS = 6; static const signed int DEFAULT_CHECKBLOCKS = 6;
static const unsigned int DEFAULT_CHECKLEVEL = 3; static const unsigned int DEFAULT_CHECKLEVEL = 3;

View File

@ -81,6 +81,7 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
result.time = wtx.GetTxTime(); result.time = wtx.GetTxTime();
result.value_map = wtx.mapValue; result.value_map = wtx.mapValue;
result.is_coinbase = wtx.IsCoinBase(); result.is_coinbase = wtx.IsCoinBase();
result.is_platform_transfer = wtx.IsPlatformTransfer();
// The determination of is_denominate is based on simplified checks here because in this part of the code // The determination of is_denominate is based on simplified checks here because in this part of the code
// we only want to know about mixing transactions belonging to this specific wallet. // we only want to know about mixing transactions belonging to this specific wallet.
result.is_denominate = wtx.tx->vin.size() == wtx.tx->vout.size() && // Number of inputs is same as number of outputs result.is_denominate = wtx.tx->vin.size() == wtx.tx->vout.size() && // Number of inputs is same as number of outputs

View File

@ -167,6 +167,8 @@ static void WalletTxToJSON(interfaces::Chain& chain, const CWalletTx& wtx, UniVa
entry.pushKV("chainlock", chainlock); entry.pushKV("chainlock", chainlock);
if (wtx.IsCoinBase()) if (wtx.IsCoinBase())
entry.pushKV("generated", true); entry.pushKV("generated", true);
if (wtx.IsPlatformTransfer())
entry.pushKV("platform-transfer", true);
if (confirms > 0) if (confirms > 0)
{ {
entry.pushKV("blockhash", wtx.m_confirm.hashBlock.GetHex()); entry.pushKV("blockhash", wtx.m_confirm.hashBlock.GetHex());
@ -1419,6 +1421,10 @@ static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx,
else else
entry.pushKV("category", "generate"); entry.pushKV("category", "generate");
} }
else if (wtx.IsPlatformTransfer())
{
entry.pushKV("category", "platform-transfer");
}
else else
{ {
entry.pushKV("category", "receive"); entry.pushKV("category", "receive");
@ -1483,7 +1489,8 @@ static RPCHelpMan listtransactions()
"\"receive\" Non-coinbase transactions received.\n" "\"receive\" Non-coinbase transactions received.\n"
"\"generate\" Coinbase transactions received with more than 100 confirmations.\n" "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
"\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n" "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
"\"orphan\" Orphaned coinbase transactions received.\n"}, "\"orphan\" Orphaned coinbase transactions received.\n"
"\"platform-transfer\" Platform Transfer transactions received.\n"},
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n" {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
"for all other categories"}, "for all other categories"},
{RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"}, {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
@ -1599,7 +1606,8 @@ static RPCHelpMan listsinceblock()
"\"receive\" Non-coinbase transactions received.\n" "\"receive\" Non-coinbase transactions received.\n"
"\"generate\" Coinbase transactions received with more than 100 confirmations.\n" "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
"\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n" "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
"\"orphan\" Orphaned coinbase transactions received.\n"}, "\"orphan\" Orphaned coinbase transactions received.\n"
"\"platform-transfer\" Platform Transfer transactions received.\n"},
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n" {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
"for all other categories"}, "for all other categories"},
{RPCResult::Type::NUM, "vout", "the vout value"}, {RPCResult::Type::NUM, "vout", "the vout value"},
@ -1740,7 +1748,8 @@ static RPCHelpMan gettransaction()
"\"receive\" Non-coinbase transactions received.\n" "\"receive\" Non-coinbase transactions received.\n"
"\"generate\" Coinbase transactions received with more than 100 confirmations.\n" "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
"\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n" "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
"\"orphan\" Orphaned coinbase transactions received.\n"}, "\"orphan\" Orphaned coinbase transactions received.\n"
"\"platform-transfer\" Platform Transfer transactions received.\n"},
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT}, {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
{RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"}, {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
{RPCResult::Type::NUM, "vout", "the vout value"}, {RPCResult::Type::NUM, "vout", "the vout value"},

View File

@ -526,8 +526,10 @@ static void TestWatchOnlyPubKey(LegacyScriptPubKeyMan* spk_man, const CPubKey& a
// Cryptographically invalidate a PubKey whilst keeping length and first byte // Cryptographically invalidate a PubKey whilst keeping length and first byte
static void PollutePubKey(CPubKey& pubkey) static void PollutePubKey(CPubKey& pubkey)
{ {
std::vector<unsigned char> pubkey_raw(pubkey.begin(), pubkey.end()); assert(pubkey.size() >= 1);
std::fill(pubkey_raw.begin()+1, pubkey_raw.end(), 0); std::vector<unsigned char> pubkey_raw;
pubkey_raw.push_back(pubkey[0]);
pubkey_raw.insert(pubkey_raw.end(), pubkey.size() - 1, 0);
pubkey = CPubKey(pubkey_raw); pubkey = CPubKey(pubkey_raw);
assert(!pubkey.IsFullyValid()); assert(!pubkey.IsFullyValid());
assert(pubkey.IsValid()); assert(pubkey.IsValid());

View File

@ -1313,7 +1313,7 @@ void CWallet::updatedBlockTip()
void CWallet::BlockUntilSyncedToCurrentChain() const { void CWallet::BlockUntilSyncedToCurrentChain() const {
AssertLockNotHeld(cs_wallet); AssertLockNotHeld(cs_wallet);
// Skip the queue-draining stuff if we know we're caught up with // Skip the queue-draining stuff if we know we're caught up with
// chainActive.Tip(), otherwise put a callback in the validation interface queue and wait // chain().Tip(), otherwise put a callback in the validation interface queue and wait
// for the queue to drain enough to execute it (indicating we are caught up // for the queue to drain enough to execute it (indicating we are caught up
// at least with the time we entered this function). // at least with the time we entered this function).
uint256 last_block_hash = WITH_LOCK(cs_wallet, return m_last_block_processed); uint256 last_block_hash = WITH_LOCK(cs_wallet, return m_last_block_processed);

View File

@ -591,6 +591,7 @@ public:
void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; } void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; }
const uint256& GetHash() const { return tx->GetHash(); } const uint256& GetHash() const { return tx->GetHash(); }
bool IsCoinBase() const { return tx->IsCoinBase(); } bool IsCoinBase() const { return tx->IsCoinBase(); }
bool IsPlatformTransfer() const { return tx->IsPlatformTransfer(); }
bool IsImmatureCoinBase() const; bool IsImmatureCoinBase() const;
// Disable copying of CWalletTx objects to prevent bugs where instances get // Disable copying of CWalletTx objects to prevent bugs where instances get

View File

@ -30,12 +30,9 @@ from test_framework.messages import (
from test_framework.script import ( from test_framework.script import (
CScript, CScript,
OP_CHECKSIG, OP_CHECKSIG,
OP_DUP,
OP_EQUALVERIFY,
OP_HASH160,
OP_RETURN, OP_RETURN,
hash160,
) )
from test_framework.script_util import key_to_p2pkh_script
from test_framework.test_framework import DashTestFramework from test_framework.test_framework import DashTestFramework
from test_framework.util import ( from test_framework.util import (
assert_equal, assert_equal,
@ -44,6 +41,7 @@ from test_framework.util import (
get_bip9_details, get_bip9_details,
hex_str_to_bytes, hex_str_to_bytes,
) )
from test_framework.wallet_util import bytes_to_wif
llmq_type_test = 106 # LLMQType::LLMQ_TEST_PLATFORM llmq_type_test = 106 # LLMQType::LLMQ_TEST_PLATFORM
tiny_amount = int(Decimal("0.0007") * COIN) tiny_amount = int(Decimal("0.0007") * COIN)
@ -66,8 +64,8 @@ class AssetLocksTest(DashTestFramework):
tmp_amount = amount tmp_amount = amount
if tmp_amount > COIN: if tmp_amount > COIN:
tmp_amount -= COIN tmp_amount -= COIN
credit_outputs.append(CTxOut(COIN, CScript([OP_DUP, OP_HASH160, hash160(pubkey), OP_EQUALVERIFY, OP_CHECKSIG]))) credit_outputs.append(CTxOut(COIN, key_to_p2pkh_script(pubkey)))
credit_outputs.append(CTxOut(tmp_amount, CScript([OP_DUP, OP_HASH160, hash160(pubkey), OP_EQUALVERIFY, OP_CHECKSIG]))) credit_outputs.append(CTxOut(tmp_amount, key_to_p2pkh_script(pubkey)))
lockTx_payload = CAssetLockTx(1, credit_outputs) lockTx_payload = CAssetLockTx(1, credit_outputs)
@ -260,6 +258,8 @@ class AssetLocksTest(DashTestFramework):
key = ECKey() key = ECKey()
key.generate() key.generate()
privkey = bytes_to_wif(key.get_bytes())
node_wallet.importprivkey(privkey)
pubkey = key.get_pubkey().get_bytes() pubkey = key.get_pubkey().get_bytes()
self.test_asset_locks(node_wallet, node, pubkey) self.test_asset_locks(node_wallet, node, pubkey)
@ -281,7 +281,11 @@ class AssetLocksTest(DashTestFramework):
self.check_mempool_result(tx=asset_lock_tx, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}}) self.check_mempool_result(tx=asset_lock_tx, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}})
self.validate_credit_pool_balance(0) self.validate_credit_pool_balance(0)
txid_in_block = self.send_tx(asset_lock_tx) txid_in_block = self.send_tx(asset_lock_tx)
assert "assetLockTx" in node.getrawtransaction(txid_in_block, 1) rpc_tx = node.getrawtransaction(txid_in_block, 1)
assert_equal(len(rpc_tx["assetLockTx"]["creditOutputs"]), 2)
assert_equal(rpc_tx["assetLockTx"]["creditOutputs"][0]["valueSat"] + rpc_tx["assetLockTx"]["creditOutputs"][1]["valueSat"], locked_1)
assert_equal(rpc_tx["assetLockTx"]["creditOutputs"][0]["scriptPubKey"]["hex"], key_to_p2pkh_script(pubkey).hex())
assert_equal(rpc_tx["assetLockTx"]["creditOutputs"][1]["scriptPubKey"]["hex"], key_to_p2pkh_script(pubkey).hex())
self.validate_credit_pool_balance(0) self.validate_credit_pool_balance(0)
node.generate(1) node.generate(1)
assert_equal(self.get_credit_pool_balance(node=node), locked_1) assert_equal(self.get_credit_pool_balance(node=node), locked_1)
@ -361,6 +365,7 @@ class AssetLocksTest(DashTestFramework):
self.wait_for_sporks_same() self.wait_for_sporks_same()
txid = self.send_tx(asset_unlock_tx) txid = self.send_tx(asset_unlock_tx)
assert_equal(node.getmempoolentry(txid)['fee'], Decimal("0.0007"))
is_id = node_wallet.sendtoaddress(node_wallet.getnewaddress(), 1) is_id = node_wallet.sendtoaddress(node_wallet.getnewaddress(), 1)
for node in self.nodes: for node in self.nodes:
self.wait_for_instantlock(is_id, node) self.wait_for_instantlock(is_id, node)
@ -399,6 +404,9 @@ class AssetLocksTest(DashTestFramework):
self.mempool_size -= 2 self.mempool_size -= 2
self.check_mempool_size() self.check_mempool_size()
block_asset_unlock = node.getrawtransaction(asset_unlock_tx.rehash(), 1)['blockhash'] block_asset_unlock = node.getrawtransaction(asset_unlock_tx.rehash(), 1)['blockhash']
self.log.info("Checking rpc `getblock` and `getblockstats` succeeds as they use own fee calculation mechanism")
assert_equal(node.getblockstats(node.getblockcount())['maxfee'], tiny_amount)
node.getblock(block_asset_unlock, 2)
self.send_tx(asset_unlock_tx, self.send_tx(asset_unlock_tx,
expected_error = "Transaction already in block chain", expected_error = "Transaction already in block chain",
@ -477,15 +485,31 @@ class AssetLocksTest(DashTestFramework):
self.check_mempool_result(tx=asset_unlock_tx_full, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}}) self.check_mempool_result(tx=asset_unlock_tx_full, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}})
txid_in_block = self.send_tx(asset_unlock_tx_full) txid_in_block = self.send_tx(asset_unlock_tx_full)
expected_balance = (Decimal(self.get_credit_pool_balance()) - Decimal(tiny_amount))
node.generate(1) node.generate(1)
self.sync_all() self.sync_all()
self.log.info("Check txid_in_block was mined...") self.log.info("Check txid_in_block was mined")
block = node.getblock(node.getbestblockhash()) block = node.getblock(node.getbestblockhash())
assert txid_in_block in block['tx'] assert txid_in_block in block['tx']
self.validate_credit_pool_balance(0) self.validate_credit_pool_balance(0)
self.log.info(f"Check status of withdrawal and try to spend it")
withdrawal_status = node_wallet.gettransaction(txid_in_block)
assert_equal(withdrawal_status['amount'] * COIN, expected_balance)
assert_equal(withdrawal_status['details'][0]['category'], 'platform-transfer')
spend_withdrawal_hex = node_wallet.createrawtransaction([{'txid': txid_in_block, 'vout' : 0}], { node_wallet.getnewaddress() : (expected_balance - Decimal(tiny_amount)) / COIN})
spend_withdrawal_hex = node_wallet.signrawtransactionwithwallet(spend_withdrawal_hex)['hex']
spend_withdrawal = tx_from_hex(spend_withdrawal_hex)
self.check_mempool_result(tx=spend_withdrawal, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}})
spend_txid_in_block = self.send_tx(spend_withdrawal)
node.generate(1)
block = node.getblock(node.getbestblockhash())
assert spend_txid_in_block in block['tx']
self.log.info("Fast forward to the next day to reset all current unlock limits...") self.log.info("Fast forward to the next day to reset all current unlock limits...")
self.slowly_generate_batch(blocks_in_one_day + 1) self.slowly_generate_batch(blocks_in_one_day)
self.mine_quorum(llmq_type_name="llmq_test_platform", llmq_type=106) self.mine_quorum(llmq_type_name="llmq_test_platform", llmq_type=106)
total = self.get_credit_pool_balance() total = self.get_credit_pool_balance()

View File

@ -17,7 +17,7 @@ from test_framework.messages import CBlock, CBlockHeader, CCbTx, CMerkleBlock, f
QuorumId, ser_uint256 QuorumId, ser_uint256
from test_framework.test_framework import DashTestFramework from test_framework.test_framework import DashTestFramework
from test_framework.util import ( from test_framework.util import (
assert_equal, p2p_port assert_equal, assert_greater_than_or_equal, p2p_port
) )
@ -46,7 +46,7 @@ class TestP2PConn(P2PInterface):
class LLMQEvoNodesTest(DashTestFramework): class LLMQEvoNodesTest(DashTestFramework):
def set_test_params(self): def set_test_params(self):
self.set_dash_test_params(5, 4, fast_dip3_enforcement=True, evo_count=7) self.set_dash_test_params(5, 4, fast_dip3_enforcement=True, evo_count=5)
self.set_dash_llmq_test_params(4, 4) self.set_dash_llmq_test_params(4, 4)
def run_test(self): def run_test(self):
@ -92,7 +92,7 @@ class LLMQEvoNodesTest(DashTestFramework):
self.mine_cycle_quorum(llmq_type_name='llmq_test_dip0024', llmq_type=103) self.mine_cycle_quorum(llmq_type_name='llmq_test_dip0024', llmq_type=103)
evo_protxhash_list = list() evo_protxhash_list = list()
for i in range(5): for i in range(self.evo_count):
evo_info = self.dynamically_add_masternode(evo=True) evo_info = self.dynamically_add_masternode(evo=True)
evo_protxhash_list.append(evo_info.proTxHash) evo_protxhash_list.append(evo_info.proTxHash)
self.nodes[0].generate(8) self.nodes[0].generate(8)
@ -115,6 +115,7 @@ class LLMQEvoNodesTest(DashTestFramework):
self.log.info("Test that EvoNodes are paid 4x blocks in a row") self.log.info("Test that EvoNodes are paid 4x blocks in a row")
self.test_evo_payments(window_analysis=48) self.test_evo_payments(window_analysis=48)
self.test_masternode_winners()
self.activate_v20() self.activate_v20()
self.activate_mn_rr() self.activate_mn_rr()
@ -127,6 +128,7 @@ class LLMQEvoNodesTest(DashTestFramework):
self.log.info("Test that EvoNodes are paid 1 block in a row after MN RewardReallocation activation") self.log.info("Test that EvoNodes are paid 1 block in a row after MN RewardReallocation activation")
self.test_evo_payments(window_analysis=48, v20active=True) self.test_evo_payments(window_analysis=48, v20active=True)
self.test_masternode_winners(mn_rr_active=True)
self.log.info(self.nodes[0].masternodelist()) self.log.info(self.nodes[0].masternodelist())
@ -248,6 +250,40 @@ class LLMQEvoNodesTest(DashTestFramework):
assert_equal(detailed_count['regular']['total'], expected_mns_count) assert_equal(detailed_count['regular']['total'], expected_mns_count)
assert_equal(detailed_count['evo']['total'], expected_evo_count) assert_equal(detailed_count['evo']['total'], expected_evo_count)
def test_masternode_winners(self, mn_rr_active=False):
# ignore recent winners, test future ones only
# we get up to 21 entries here: tip + up to 20 future payees
winners = self.nodes[0].masternode('winners', '0')
weighted_count = self.mn_count + self.evo_count * (1 if mn_rr_active else 4)
assert_equal(len(winners.keys()) - 1, 20 if weighted_count > 20 else weighted_count)
consecutive_payments = 0
full_consecutive_payments_found = 0
payment_cycles = 0
first_payee = None
prev_winner = None
for height in winners.keys():
winner = winners[height]
if mn_rr_active:
assert_equal(prev_winner == winner, False)
else:
if prev_winner == winner:
consecutive_payments += 1
else:
if consecutive_payments == 3:
full_consecutive_payments_found += 1
consecutive_payments = 0
assert_greater_than_or_equal(3, consecutive_payments)
if consecutive_payments == 0 and winner == first_payee:
payment_cycles += 1
if first_payee is None:
first_payee = winner
prev_winner = winner
if mn_rr_active:
assert_equal(full_consecutive_payments_found, 0)
else:
assert_greater_than_or_equal(full_consecutive_payments_found, (len(winners.keys()) - 1 - self.mn_count) // 4 - 1)
assert_equal(payment_cycles, (len(winners.keys()) - 1) // weighted_count)
def test_getmnlistdiff(self, baseBlockHash, blockHash, baseMNList, expectedDeleted, expectedUpdated): def test_getmnlistdiff(self, baseBlockHash, blockHash, baseMNList, expectedDeleted, expectedUpdated):
d = self.test_getmnlistdiff_base(baseBlockHash, blockHash) d = self.test_getmnlistdiff_base(baseBlockHash, blockHash)