mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 22:26:42 +00:00
dev: add: merge develop in upstream-merge
This commit is contained in:
commit
1c106dc251
82 changed files with 5528 additions and 583 deletions
9
.github/matic-cli-config.yml
vendored
9
.github/matic-cli-config.yml
vendored
|
|
@ -10,8 +10,12 @@ blockNumber:
|
||||||
- '0'
|
- '0'
|
||||||
blockTime:
|
blockTime:
|
||||||
- '2'
|
- '2'
|
||||||
numOfValidators: 3
|
numOfBorValidators: 3
|
||||||
numOfNonValidators: 0
|
numOfBorSentries: 0
|
||||||
|
numOfBorArchiveNodes: 0
|
||||||
|
numOfErigonValidators: 0
|
||||||
|
numOfErigonSentries: 0
|
||||||
|
numOfErigonArchiveNodes: 0
|
||||||
ethURL: http://ganache:9545
|
ethURL: http://ganache:9545
|
||||||
ethHostUser: ubuntu
|
ethHostUser: ubuntu
|
||||||
devnetType: docker
|
devnetType: docker
|
||||||
|
|
@ -19,4 +23,3 @@ borDockerBuildContext: "../../bor"
|
||||||
heimdallDockerBuildContext: "https://github.com/maticnetwork/heimdall.git#develop"
|
heimdallDockerBuildContext: "https://github.com/maticnetwork/heimdall.git#develop"
|
||||||
sprintSizeBlockNumber:
|
sprintSizeBlockNumber:
|
||||||
- '0'
|
- '0'
|
||||||
numOfArchiveNodes: 0
|
|
||||||
|
|
|
||||||
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
|
|
@ -23,6 +23,8 @@ In case this PR includes changes that must be applied only to a subset of nodes,
|
||||||
- [ ] I have added at least 2 reviewer or the whole pos-v1 team
|
- [ ] I have added at least 2 reviewer or the whole pos-v1 team
|
||||||
- [ ] I have added sufficient documentation in code
|
- [ ] I have added sufficient documentation in code
|
||||||
- [ ] I will be resolving comments - if any - by pushing each fix in a separate commit and linking the commit hash in the comment reply
|
- [ ] I will be resolving comments - if any - by pushing each fix in a separate commit and linking the commit hash in the comment reply
|
||||||
|
- [ ] Created a task in Jira and informed the team for implementation in Erigon client (if applicable)
|
||||||
|
- [ ] Includes RPC methods changes, and the Notion documentation has been updated
|
||||||
|
|
||||||
# Cross repository changes
|
# Cross repository changes
|
||||||
|
|
||||||
|
|
|
||||||
220
.github/workflows/packager.yml
vendored
220
.github/workflows/packager.yml
vendored
|
|
@ -61,116 +61,6 @@ jobs:
|
||||||
- name: Copying systemd file
|
- name: Copying systemd file
|
||||||
run: cp -rp packaging/templates/systemd/bor.service packaging/deb/bor/lib/systemd/system/bor.service
|
run: cp -rp packaging/templates/systemd/bor.service packaging/deb/bor/lib/systemd/system/bor.service
|
||||||
|
|
||||||
- name: Prepping ${{ env.NETWORK }} ${{ env.NODE }} bootnode for ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/deb/bor packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: any
|
|
||||||
- name: Putting toml for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/testnet-v4/sentry/sentry/bor/config.toml packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/var/lib/bor/
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mumbai
|
|
||||||
- name: Copying the preinst for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/preinst packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/preinst
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: any
|
|
||||||
- name: Copying the postinst for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/postinst.profile packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/postinst
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: any
|
|
||||||
- name: Copying the prerm for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/prerm packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/prerm
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: any
|
|
||||||
- name: Copying the postrm for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/postrm packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/postrm
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: any
|
|
||||||
- name: Copying systemd file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/systemd/bor_bootnode.service packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/lib/systemd/system/
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: any
|
|
||||||
- name: Copying profile control file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/control.profile.amd64 packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/control
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mumbai
|
|
||||||
- name: Running package build for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: dpkg-deb --build --root-owner-group packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mumbai
|
|
||||||
|
|
||||||
- name: Prepping ${{ env.NETWORK }} ${{ env.NODE }} bootnode for ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/deb/bor packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
- name: Putting toml for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/var/lib/bor/
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
- name: Copying the preinst for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/preinst packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/preinst
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
- name: Copying the postinst for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/postinst.profile packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/postinst
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
- name: Copying the prerm for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/prerm packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/prerm
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
- name: Copying the postrm for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/postrm packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/postrm
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
- name: Copying systemd file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/systemd/bor_bootnode.service packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/lib/systemd/system/
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
- name: Copying profile control file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/control.profile.amd64 packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/control
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
- name: Running package build for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: dpkg-deb --build --root-owner-group packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
|
|
||||||
env:
|
|
||||||
ARCH: amd64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
|
|
||||||
- name: Prepping ${{ env.NETWORK }} ${{ env.NODE }} node for ${{ env.ARCH }}
|
- name: Prepping ${{ env.NETWORK }} ${{ env.NODE }} node for ${{ env.ARCH }}
|
||||||
run: cp -rp packaging/deb/bor packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
|
run: cp -rp packaging/deb/bor packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
|
||||||
env:
|
env:
|
||||||
|
|
@ -526,116 +416,6 @@ jobs:
|
||||||
- name: Updating the control file to use with the arm64 profile
|
- name: Updating the control file to use with the arm64 profile
|
||||||
run: cp -rp packaging/templates/package_scripts/control.profile.arm64 packaging/deb/bor/DEBIAN/control
|
run: cp -rp packaging/templates/package_scripts/control.profile.arm64 packaging/deb/bor/DEBIAN/control
|
||||||
|
|
||||||
- name: Prepping ${{ env.NETWORK }} ${{ env.NODE }} bootnode for ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/deb/bor packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mumbai
|
|
||||||
- name: Putting toml for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/testnet-v4/sentry/sentry/bor/config.toml packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/var/lib/bor/
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mumbai
|
|
||||||
- name: Copying the preinst for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/preinst packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/preinst
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mumbai
|
|
||||||
- name: Copying the postinst for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/postinst.profile packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/postinst
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mumbai
|
|
||||||
- name: Copying the prerm for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/prerm packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/prerm
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mumbai
|
|
||||||
- name: Copying the postrm for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/postrm packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/postrm
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mumbai
|
|
||||||
- name: Copying systemd file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/systemd/bor_bootnode.service packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/lib/systemd/system/
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mumbai
|
|
||||||
- name: Copying profile control file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/control.profile.amd64 packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/control
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mumbai
|
|
||||||
- name: Running package build for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: dpkg-deb --build --root-owner-group packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mumbai
|
|
||||||
|
|
||||||
- name: Prepping ${{ env.NETWORK }} ${{ env.NODE }} bootnode for ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/deb/bor packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
- name: Putting toml for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/var/lib/bor/
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
- name: Copying the preinst for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/preinst packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/preinst
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
- name: Copying the postinst for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/postinst.profile packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/postinst
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
- name: Copying the prerm for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/prerm packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/prerm
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
- name: Copying the postrm for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/postrm packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/postrm
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
- name: Copying systemd file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/systemd/bor_bootnode.service packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/lib/systemd/system/
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
- name: Copying profile control file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: cp -rp packaging/templates/package_scripts/control.profile.amd64 packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/control
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
- name: Running package build for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
|
||||||
run: dpkg-deb --build --root-owner-group packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
|
|
||||||
env:
|
|
||||||
ARCH: arm64
|
|
||||||
NODE: bootnode
|
|
||||||
NETWORK: mainnet
|
|
||||||
|
|
||||||
- name: Setting up bor for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
- name: Setting up bor for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
|
||||||
run: cp -rp packaging/deb/bor packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
|
run: cp -rp packaging/deb/bor packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
|
||||||
env:
|
env:
|
||||||
|
|
|
||||||
2
.github/workflows/security-ci.yml
vendored
2
.github/workflows/security-ci.yml
vendored
|
|
@ -49,7 +49,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Running govulncheck
|
- name: Running govulncheck
|
||||||
uses: Templum/govulncheck-action@v0.0.8
|
uses: Templum/govulncheck-action@v0.10.1
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
env:
|
env:
|
||||||
DEBUG: "true"
|
DEBUG: "true"
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ syncmode = "full"
|
||||||
# etherbase = "VALIDATOR ADDRESS"
|
# etherbase = "VALIDATOR ADDRESS"
|
||||||
# extradata = ""
|
# extradata = ""
|
||||||
# recommit = "2m5s"
|
# recommit = "2m5s"
|
||||||
|
# commitinterrupt = true
|
||||||
|
|
||||||
|
|
||||||
# [jsonrpc]
|
# [jsonrpc]
|
||||||
|
|
@ -128,7 +129,7 @@ syncmode = "full"
|
||||||
metrics = true
|
metrics = true
|
||||||
# expensive = false
|
# expensive = false
|
||||||
# prometheus-addr = "127.0.0.1:7071"
|
# prometheus-addr = "127.0.0.1:7071"
|
||||||
# opencollector-endpoint = "127.0.0.1:4317"
|
# opencollector-endpoint = ""
|
||||||
# [telemetry.influx]
|
# [telemetry.influx]
|
||||||
# influxdb = false
|
# influxdb = false
|
||||||
# endpoint = ""
|
# endpoint = ""
|
||||||
|
|
@ -171,6 +172,10 @@ syncmode = "full"
|
||||||
# period = 0
|
# period = 0
|
||||||
# gaslimit = 11500000
|
# gaslimit = 11500000
|
||||||
|
|
||||||
|
# [parallelevm]
|
||||||
|
# enable = true
|
||||||
|
# procs = 8
|
||||||
|
|
||||||
# [pprof]
|
# [pprof]
|
||||||
# pprof = false
|
# pprof = false
|
||||||
# port = 6060
|
# port = 6060
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,11 @@
|
||||||
"bor": {
|
"bor": {
|
||||||
"jaipurBlock": 22770000,
|
"jaipurBlock": 22770000,
|
||||||
"delhiBlock": 29638656,
|
"delhiBlock": 29638656,
|
||||||
|
"parallelUniverseBlock": 0,
|
||||||
|
"indoreBlock": 36877056,
|
||||||
|
"stateSyncConfirmationDelay": {
|
||||||
|
"36877056": 128
|
||||||
|
},
|
||||||
"period": {
|
"period": {
|
||||||
"0": 2,
|
"0": 2,
|
||||||
"25275000": 5,
|
"25275000": 5,
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
@ -11,4 +12,5 @@ import (
|
||||||
//go:generate mockgen -destination=./caller_mock.go -package=api . Caller
|
//go:generate mockgen -destination=./caller_mock.go -package=api . Caller
|
||||||
type Caller interface {
|
type Caller interface {
|
||||||
Call(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *ethapi.StateOverride) (hexutil.Bytes, error)
|
Call(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *ethapi.StateOverride) (hexutil.Bytes, error)
|
||||||
|
CallWithState(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, state *state.StateDB, overrides *ethapi.StateOverride) (hexutil.Bytes, error)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
|
|
||||||
hexutil "github.com/ethereum/go-ethereum/common/hexutil"
|
hexutil "github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
state "github.com/ethereum/go-ethereum/core/state"
|
||||||
ethapi "github.com/ethereum/go-ethereum/internal/ethapi"
|
ethapi "github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
rpc "github.com/ethereum/go-ethereum/rpc"
|
rpc "github.com/ethereum/go-ethereum/rpc"
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
|
@ -51,3 +52,18 @@ func (mr *MockCallerMockRecorder) Call(arg0, arg1, arg2, arg3 interface{}) *gomo
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Call", reflect.TypeOf((*MockCaller)(nil).Call), arg0, arg1, arg2, arg3)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Call", reflect.TypeOf((*MockCaller)(nil).Call), arg0, arg1, arg2, arg3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CallWithState mocks base method.
|
||||||
|
func (m *MockCaller) CallWithState(arg0 context.Context, arg1 ethapi.TransactionArgs, arg2 rpc.BlockNumberOrHash, arg3 *state.StateDB, arg4 *ethapi.StateOverride) (hexutil.Bytes, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "CallWithState", arg0, arg1, arg2, arg3, arg4)
|
||||||
|
ret0, _ := ret[0].(hexutil.Bytes)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallWithState indicates an expected call of CallWithState.
|
||||||
|
func (mr *MockCallerMockRecorder) CallWithState(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CallWithState", reflect.TypeOf((*MockCaller)(nil).CallWithState), arg0, arg1, arg2, arg3, arg4)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1179,22 +1179,42 @@ func (c *Bor) CommitStates(
|
||||||
fetchStart := time.Now()
|
fetchStart := time.Now()
|
||||||
number := header.Number.Uint64()
|
number := header.Number.Uint64()
|
||||||
|
|
||||||
_lastStateID, err := c.GenesisContractsClient.LastStateId(number - 1)
|
var (
|
||||||
if err != nil {
|
lastStateIDBig *big.Int
|
||||||
return nil, err
|
from uint64
|
||||||
|
to time.Time
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if c.config.IsIndore(header.Number) {
|
||||||
|
// Fetch the LastStateId from contract via current state instance
|
||||||
|
lastStateIDBig, err = c.GenesisContractsClient.LastStateId(state.Copy(), number-1, header.ParentHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stateSyncDelay := c.config.CalculateStateSyncDelay(number)
|
||||||
|
to = time.Unix(int64(header.Time-stateSyncDelay), 0)
|
||||||
|
} else {
|
||||||
|
lastStateIDBig, err = c.GenesisContractsClient.LastStateId(nil, number-1, header.ParentHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
to = time.Unix(int64(chain.Chain.GetHeaderByNumber(number-c.config.CalculateSprint(number)).Time), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
to := time.Unix(int64(chain.Chain.GetHeaderByNumber(number-c.config.CalculateSprint(number)).Time), 0)
|
lastStateID := lastStateIDBig.Uint64()
|
||||||
lastStateID := _lastStateID.Uint64()
|
from = lastStateID + 1
|
||||||
|
|
||||||
log.Info(
|
log.Info(
|
||||||
"Fetching state updates from Heimdall",
|
"Fetching state updates from Heimdall",
|
||||||
"fromID", lastStateID+1,
|
"fromID", from,
|
||||||
"to", to.Format(time.RFC3339))
|
"to", to.Format(time.RFC3339))
|
||||||
|
|
||||||
eventRecords, err := c.HeimdallClient.StateSyncEvents(ctx, lastStateID+1, to.Unix())
|
eventRecords, err := c.HeimdallClient.StateSyncEvents(ctx, from, to.Unix())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error occurred when fetching state sync events", "stateID", lastStateID+1, "error", err)
|
log.Error("Error occurred when fetching state sync events", "fromID", from, "to", to.Unix(), "err", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.config.OverrideStateSyncRecords != nil {
|
if c.config.OverrideStateSyncRecords != nil {
|
||||||
|
|
@ -1217,7 +1237,7 @@ func (c *Bor) CommitStates(
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = validateEventRecord(eventRecord, number, to, lastStateID, chainID); err != nil {
|
if err = validateEventRecord(eventRecord, number, to, lastStateID, chainID); err != nil {
|
||||||
log.Error("while validating event record", "block", number, "to", to, "stateID", lastStateID, "error", err.Error())
|
log.Error("while validating event record", "block", number, "to", to, "stateID", lastStateID+1, "error", err.Error())
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -100,8 +100,8 @@ func (gc *GenesisContractsClient) CommitState(
|
||||||
return gasUsed, nil
|
return gasUsed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gc *GenesisContractsClient) LastStateId(snapshotNumber uint64) (*big.Int, error) {
|
func (gc *GenesisContractsClient) LastStateId(state *state.StateDB, number uint64, hash common.Hash) (*big.Int, error) {
|
||||||
blockNr := rpc.BlockNumber(snapshotNumber)
|
blockNr := rpc.BlockNumber(number)
|
||||||
|
|
||||||
const method = "lastStateId"
|
const method = "lastStateId"
|
||||||
|
|
||||||
|
|
@ -116,11 +116,13 @@ func (gc *GenesisContractsClient) LastStateId(snapshotNumber uint64) (*big.Int,
|
||||||
toAddress := common.HexToAddress(gc.StateReceiverContract)
|
toAddress := common.HexToAddress(gc.StateReceiverContract)
|
||||||
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
|
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
|
||||||
|
|
||||||
result, err := gc.ethAPI.Call(context.Background(), ethapi.TransactionArgs{
|
// Do a call with state so that we can fetch the last state ID from a given (incoming)
|
||||||
|
// state instead of local(canonical) chain.
|
||||||
|
result, err := gc.ethAPI.CallWithState(context.Background(), ethapi.TransactionArgs{
|
||||||
Gas: &gas,
|
Gas: &gas,
|
||||||
To: &toAddress,
|
To: &toAddress,
|
||||||
Data: &msgData,
|
Data: &msgData,
|
||||||
}, rpc.BlockNumberOrHash{BlockNumber: &blockNr}, nil)
|
}, rpc.BlockNumberOrHash{BlockNumber: &blockNr, BlockHash: &hash}, state, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package bor
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
|
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor/statefull"
|
"github.com/ethereum/go-ethereum/consensus/bor/statefull"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
|
@ -12,5 +13,5 @@ import (
|
||||||
//go:generate mockgen -destination=./genesis_contract_mock.go -package=bor . GenesisContract
|
//go:generate mockgen -destination=./genesis_contract_mock.go -package=bor . GenesisContract
|
||||||
type GenesisContract interface {
|
type GenesisContract interface {
|
||||||
CommitState(event *clerk.EventRecordWithTime, state *state.StateDB, header *types.Header, chCtx statefull.ChainContext) (uint64, error)
|
CommitState(event *clerk.EventRecordWithTime, state *state.StateDB, header *types.Header, chCtx statefull.ChainContext) (uint64, error)
|
||||||
LastStateId(snapshotNumber uint64) (*big.Int, error)
|
LastStateId(state *state.StateDB, number uint64, hash common.Hash) (*big.Int, error)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
big "math/big"
|
big "math/big"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
|
|
||||||
|
common "github.com/ethereum/go-ethereum/common"
|
||||||
clerk "github.com/ethereum/go-ethereum/consensus/bor/clerk"
|
clerk "github.com/ethereum/go-ethereum/consensus/bor/clerk"
|
||||||
statefull "github.com/ethereum/go-ethereum/consensus/bor/statefull"
|
statefull "github.com/ethereum/go-ethereum/consensus/bor/statefull"
|
||||||
state "github.com/ethereum/go-ethereum/core/state"
|
state "github.com/ethereum/go-ethereum/core/state"
|
||||||
|
|
@ -54,16 +55,16 @@ func (mr *MockGenesisContractMockRecorder) CommitState(arg0, arg1, arg2, arg3 in
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastStateId mocks base method.
|
// LastStateId mocks base method.
|
||||||
func (m *MockGenesisContract) LastStateId(arg0 uint64) (*big.Int, error) {
|
func (m *MockGenesisContract) LastStateId(arg0 *state.StateDB, arg1 uint64, arg2 common.Hash) (*big.Int, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "LastStateId", arg0)
|
ret := m.ctrl.Call(m, "LastStateId", arg0, arg1, arg2)
|
||||||
ret0, _ := ret[0].(*big.Int)
|
ret0, _ := ret[0].(*big.Int)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastStateId indicates an expected call of LastStateId.
|
// LastStateId indicates an expected call of LastStateId.
|
||||||
func (mr *MockGenesisContractMockRecorder) LastStateId(arg0 interface{}) *gomock.Call {
|
func (mr *MockGenesisContractMockRecorder) LastStateId(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LastStateId", reflect.TypeOf((*MockGenesisContract)(nil).LastStateId), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LastStateId", reflect.TypeOf((*MockGenesisContract)(nil).LastStateId), arg0, arg1, arg2)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Code generated by MockGen. DO NOT EDIT.
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
// Source: consensus/bor/span.go
|
// Source: github.com/ethereum/go-ethereum/consensus/bor (interfaces: Spanner)
|
||||||
|
|
||||||
// Package bor is a generated GoMock package.
|
// Package bor is a generated GoMock package.
|
||||||
package bor
|
package bor
|
||||||
|
|
@ -42,60 +42,60 @@ func (m *MockSpanner) EXPECT() *MockSpannerMockRecorder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitSpan mocks base method.
|
// CommitSpan mocks base method.
|
||||||
func (m *MockSpanner) CommitSpan(ctx context.Context, heimdallSpan span.HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error {
|
func (m *MockSpanner) CommitSpan(arg0 context.Context, arg1 span.HeimdallSpan, arg2 *state.StateDB, arg3 *types.Header, arg4 core.ChainContext) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "CommitSpan", ctx, heimdallSpan, state, header, chainContext)
|
ret := m.ctrl.Call(m, "CommitSpan", arg0, arg1, arg2, arg3, arg4)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitSpan indicates an expected call of CommitSpan.
|
// CommitSpan indicates an expected call of CommitSpan.
|
||||||
func (mr *MockSpannerMockRecorder) CommitSpan(ctx, heimdallSpan, state, header, chainContext interface{}) *gomock.Call {
|
func (mr *MockSpannerMockRecorder) CommitSpan(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitSpan", reflect.TypeOf((*MockSpanner)(nil).CommitSpan), ctx, heimdallSpan, state, header, chainContext)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitSpan", reflect.TypeOf((*MockSpanner)(nil).CommitSpan), arg0, arg1, arg2, arg3, arg4)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentSpan mocks base method.
|
// GetCurrentSpan mocks base method.
|
||||||
func (m *MockSpanner) GetCurrentSpan(ctx context.Context, headerHash common.Hash) (*span.Span, error) {
|
func (m *MockSpanner) GetCurrentSpan(arg0 context.Context, arg1 common.Hash) (*span.Span, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "GetCurrentSpan", ctx, headerHash)
|
ret := m.ctrl.Call(m, "GetCurrentSpan", arg0, arg1)
|
||||||
ret0, _ := ret[0].(*span.Span)
|
ret0, _ := ret[0].(*span.Span)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentSpan indicates an expected call of GetCurrentSpan.
|
// GetCurrentSpan indicates an expected call of GetCurrentSpan.
|
||||||
func (mr *MockSpannerMockRecorder) GetCurrentSpan(ctx, headerHash interface{}) *gomock.Call {
|
func (mr *MockSpannerMockRecorder) GetCurrentSpan(arg0, arg1 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentSpan", reflect.TypeOf((*MockSpanner)(nil).GetCurrentSpan), ctx, headerHash)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentSpan", reflect.TypeOf((*MockSpanner)(nil).GetCurrentSpan), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentValidatorsByBlockNrOrHash mocks base method.
|
// GetCurrentValidatorsByBlockNrOrHash mocks base method.
|
||||||
func (m *MockSpanner) GetCurrentValidatorsByBlockNrOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, blockNumber uint64) ([]*valset.Validator, error) {
|
func (m *MockSpanner) GetCurrentValidatorsByBlockNrOrHash(arg0 context.Context, arg1 rpc.BlockNumberOrHash, arg2 uint64) ([]*valset.Validator, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "GetCurrentValidatorsByBlockNrOrHash", ctx, blockNrOrHash, blockNumber)
|
ret := m.ctrl.Call(m, "GetCurrentValidatorsByBlockNrOrHash", arg0, arg1, arg2)
|
||||||
ret0, _ := ret[0].([]*valset.Validator)
|
ret0, _ := ret[0].([]*valset.Validator)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentValidatorsByBlockNrOrHash indicates an expected call of GetCurrentValidatorsByBlockNrOrHash.
|
// GetCurrentValidatorsByBlockNrOrHash indicates an expected call of GetCurrentValidatorsByBlockNrOrHash.
|
||||||
func (mr *MockSpannerMockRecorder) GetCurrentValidatorsByBlockNrOrHash(ctx, blockNrOrHash, blockNumber interface{}) *gomock.Call {
|
func (mr *MockSpannerMockRecorder) GetCurrentValidatorsByBlockNrOrHash(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidatorsByBlockNrOrHash", reflect.TypeOf((*MockSpanner)(nil).GetCurrentValidatorsByBlockNrOrHash), ctx, blockNrOrHash, blockNumber)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidatorsByBlockNrOrHash", reflect.TypeOf((*MockSpanner)(nil).GetCurrentValidatorsByBlockNrOrHash), arg0, arg1, arg2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentValidatorsByHash mocks base method.
|
// GetCurrentValidatorsByHash mocks base method.
|
||||||
func (m *MockSpanner) GetCurrentValidatorsByHash(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) {
|
func (m *MockSpanner) GetCurrentValidatorsByHash(arg0 context.Context, arg1 common.Hash, arg2 uint64) ([]*valset.Validator, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "GetCurrentValidatorsByHash", ctx, headerHash, blockNumber)
|
ret := m.ctrl.Call(m, "GetCurrentValidatorsByHash", arg0, arg1, arg2)
|
||||||
ret0, _ := ret[0].([]*valset.Validator)
|
ret0, _ := ret[0].([]*valset.Validator)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentValidatorsByHash indicates an expected call of GetCurrentValidatorsByHash.
|
// GetCurrentValidatorsByHash indicates an expected call of GetCurrentValidatorsByHash.
|
||||||
func (mr *MockSpannerMockRecorder) GetCurrentValidatorsByHash(ctx, headerHash, blockNumber interface{}) *gomock.Call {
|
func (mr *MockSpannerMockRecorder) GetCurrentValidatorsByHash(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidatorsByHash", reflect.TypeOf((*MockSpanner)(nil).GetCurrentValidatorsByHash), ctx, headerHash, blockNumber)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidatorsByHash", reflect.TypeOf((*MockSpanner)(nil).GetCurrentValidatorsByHash), arg0, arg1, arg2)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/prque"
|
"github.com/ethereum/go-ethereum/common/prque"
|
||||||
"github.com/ethereum/go-ethereum/common/tracing"
|
"github.com/ethereum/go-ethereum/common/tracing"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
|
"github.com/ethereum/go-ethereum/core/blockstm"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||||
|
|
@ -84,10 +85,12 @@ var (
|
||||||
blockImportTimer = metrics.NewRegisteredMeter("chain/imports", nil)
|
blockImportTimer = metrics.NewRegisteredMeter("chain/imports", nil)
|
||||||
triedbCommitTimer = metrics.NewRegisteredTimer("chain/triedb/commits", nil)
|
triedbCommitTimer = metrics.NewRegisteredTimer("chain/triedb/commits", nil)
|
||||||
|
|
||||||
blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil)
|
blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil)
|
||||||
blockValidationTimer = metrics.NewRegisteredTimer("chain/validation", nil)
|
blockValidationTimer = metrics.NewRegisteredTimer("chain/validation", nil)
|
||||||
blockExecutionTimer = metrics.NewRegisteredTimer("chain/execution", nil)
|
blockExecutionTimer = metrics.NewRegisteredTimer("chain/execution", nil)
|
||||||
blockWriteTimer = metrics.NewRegisteredTimer("chain/write", nil)
|
blockWriteTimer = metrics.NewRegisteredTimer("chain/write", nil)
|
||||||
|
blockExecutionParallelCounter = metrics.NewRegisteredCounter("chain/execution/parallel", nil)
|
||||||
|
blockExecutionSerialCounter = metrics.NewRegisteredCounter("chain/execution/serial", nil)
|
||||||
|
|
||||||
blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil)
|
blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil)
|
||||||
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
|
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
|
||||||
|
|
@ -230,12 +233,13 @@ type BlockChain struct {
|
||||||
stopping atomic.Bool // false if chain is running, true when stopped
|
stopping atomic.Bool // false if chain is running, true when stopped
|
||||||
procInterrupt atomic.Bool // interrupt signaler for block processing
|
procInterrupt atomic.Bool // interrupt signaler for block processing
|
||||||
|
|
||||||
engine consensus.Engine
|
engine consensus.Engine
|
||||||
validator Validator // Block and state validator interface
|
validator Validator // Block and state validator interface
|
||||||
prefetcher Prefetcher
|
prefetcher Prefetcher
|
||||||
processor Processor // Block transaction processor interface
|
processor Processor // Block transaction processor interface
|
||||||
forker *ForkChoice
|
parallelProcessor Processor // Parallel block transaction processor interface
|
||||||
vmConfig vm.Config
|
forker *ForkChoice
|
||||||
|
vmConfig vm.Config
|
||||||
|
|
||||||
// Bor related changes
|
// Bor related changes
|
||||||
borReceiptsCache *lru.Cache[common.Hash, *types.Receipt] // Cache for the most recent bor receipt receipts per block
|
borReceiptsCache *lru.Cache[common.Hash, *types.Receipt] // Cache for the most recent bor receipt receipts per block
|
||||||
|
|
@ -467,6 +471,94 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
return bc, nil
|
return bc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewParallelBlockChain , similar to NewBlockChain, creates a new blockchain object, but with a parallel state processor
|
||||||
|
// TODO marcello check this func
|
||||||
|
func NewParallelBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(header *types.Header) bool, txLookupLimit *uint64, checker ethereum.ChainValidator) (*BlockChain, error) {
|
||||||
|
bc, err := NewBlockChain(db, cacheConfig, chainConfig, engine, vmConfig, shouldPreserve, txLookupLimit, checker)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bc.parallelProcessor = NewParallelStateProcessor(chainConfig, bc, engine)
|
||||||
|
|
||||||
|
return bc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *BlockChain) ProcessBlock(block *types.Block, parent *types.Header) (types.Receipts, []*types.Log, uint64, *state.StateDB, error) {
|
||||||
|
// Process the block using processor and parallelProcessor at the same time, take the one which finishes first, cancel the other, and return the result
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
type Result struct {
|
||||||
|
receipts types.Receipts
|
||||||
|
logs []*types.Log
|
||||||
|
usedGas uint64
|
||||||
|
err error
|
||||||
|
statedb *state.StateDB
|
||||||
|
counter metrics.Counter
|
||||||
|
}
|
||||||
|
|
||||||
|
resultChan := make(chan Result, 2)
|
||||||
|
|
||||||
|
processorCount := 0
|
||||||
|
|
||||||
|
if bc.parallelProcessor != nil {
|
||||||
|
parallelStatedb, err := state.New(parent.Root, bc.stateCache, bc.snaps)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
processorCount++
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
parallelStatedb.StartPrefetcher("chain")
|
||||||
|
receipts, logs, usedGas, err := bc.parallelProcessor.Process(block, parallelStatedb, bc.vmConfig, ctx)
|
||||||
|
resultChan <- Result{receipts, logs, usedGas, err, parallelStatedb, blockExecutionParallelCounter}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if bc.processor != nil {
|
||||||
|
statedb, err := state.New(parent.Root, bc.stateCache, bc.snaps)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
processorCount++
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
statedb.StartPrefetcher("chain")
|
||||||
|
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig, ctx)
|
||||||
|
resultChan <- Result{receipts, logs, usedGas, err, statedb, blockExecutionSerialCounter}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
result := <-resultChan
|
||||||
|
|
||||||
|
if _, ok := result.err.(blockstm.ParallelExecFailedError); ok {
|
||||||
|
log.Warn("Parallel state processor failed", "err", result.err)
|
||||||
|
|
||||||
|
// If the parallel processor failed, we will fallback to the serial processor if enabled
|
||||||
|
if processorCount == 2 {
|
||||||
|
result.statedb.StopPrefetcher()
|
||||||
|
result = <-resultChan
|
||||||
|
processorCount--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.counter.Inc(1)
|
||||||
|
|
||||||
|
// Make sure we are not leaking any prefetchers
|
||||||
|
if processorCount == 2 {
|
||||||
|
go func() {
|
||||||
|
second_result := <-resultChan
|
||||||
|
second_result.statedb.StopPrefetcher()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.receipts, result.logs, result.usedGas, result.statedb, result.err
|
||||||
|
}
|
||||||
|
|
||||||
// empty returns an indicator whether the blockchain is empty.
|
// empty returns an indicator whether the blockchain is empty.
|
||||||
// Note, it's a special case that we connect a non-empty ancient
|
// Note, it's a special case that we connect a non-empty ancient
|
||||||
// database with an empty node, so that we can plugin the ancient
|
// database with an empty node, so that we can plugin the ancient
|
||||||
|
|
@ -1933,7 +2025,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool)
|
||||||
|
|
||||||
// Process block using the parent state as reference point
|
// Process block using the parent state as reference point
|
||||||
pstart := time.Now()
|
pstart := time.Now()
|
||||||
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig)
|
receipts, logs, usedGas, statedb, err := bc.ProcessBlock(block, parent)
|
||||||
|
activeState = statedb
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bc.reportBlock(block, receipts, err)
|
bc.reportBlock(block, receipts, err)
|
||||||
followupInterrupt.Store(true)
|
followupInterrupt.Store(true)
|
||||||
|
|
@ -2769,7 +2862,6 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i
|
||||||
return 0, errChainStopped
|
return 0, errChainStopped
|
||||||
}
|
}
|
||||||
defer bc.chainmu.Unlock()
|
defer bc.chainmu.Unlock()
|
||||||
// TODO marcello check headerchain method
|
|
||||||
_, err := bc.hc.InsertHeaderChain(chain, start, bc.forker)
|
_, err := bc.hc.InsertHeaderChain(chain, start, bc.forker)
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -158,11 +157,9 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), blockchain.stateCache, nil)
|
|
||||||
if err != nil {
|
receipts, _, usedGas, statedb, err := blockchain.ProcessBlock(block, blockchain.GetBlockByHash(block.ParentHash()).Header())
|
||||||
return err
|
|
||||||
}
|
|
||||||
receipts, _, usedGas, err := blockchain.processor.Process(block, statedb, vm.Config{})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
blockchain.reportBlock(block, receipts, err)
|
blockchain.reportBlock(block, receipts, err)
|
||||||
return err
|
return err
|
||||||
|
|
@ -182,6 +179,26 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParallelBlockChainImport(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
db, _, blockchain, err := newCanonical(ethash.NewFaker(), 10, true)
|
||||||
|
blockchain.parallelProcessor = NewParallelStateProcessor(blockchain.chainConfig, blockchain, blockchain.engine)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to make new canonical chain: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer blockchain.Stop()
|
||||||
|
|
||||||
|
block := blockchain.GetBlockByHash(blockchain.CurrentBlock().Hash())
|
||||||
|
blockChainB := makeFakeNonEmptyBlockChain(block, 5, ethash.NewFaker(), db, forkSeed, 5)
|
||||||
|
|
||||||
|
if err := testBlockChainImport(blockChainB, blockchain); err == nil {
|
||||||
|
t.Fatalf("expected error for bad tx")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// testHeaderChainImport tries to process a chain of header, writing them into
|
// testHeaderChainImport tries to process a chain of header, writing them into
|
||||||
// the database if successful.
|
// the database if successful.
|
||||||
func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error {
|
func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error {
|
||||||
|
|
@ -2027,7 +2044,6 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
||||||
fork, _ := GenerateChain(gspec.Config, parent, engine, genDb, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) {
|
fork, _ := GenerateChain(gspec.Config, parent, engine, genDb, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) {
|
||||||
b.SetCoinbase(common.Address{2})
|
b.SetCoinbase(common.Address{2})
|
||||||
if int(b.header.Number.Uint64()) >= mergeBlock {
|
if int(b.header.Number.Uint64()) >= mergeBlock {
|
||||||
// TODO marcello SetPoS everywhere
|
|
||||||
b.SetPoS()
|
b.SetPoS()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
201
core/blockstm/dag.go
Normal file
201
core/blockstm/dag.go
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
package blockstm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/heimdalr/dag"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DAG struct {
|
||||||
|
*dag.DAG
|
||||||
|
}
|
||||||
|
|
||||||
|
type TxDep struct {
|
||||||
|
Index int
|
||||||
|
ReadList []ReadDescriptor
|
||||||
|
FullWriteList [][]WriteDescriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
func HasReadDep(txFrom TxnOutput, txTo TxnInput) bool {
|
||||||
|
reads := make(map[Key]bool)
|
||||||
|
|
||||||
|
for _, v := range txTo {
|
||||||
|
reads[v.Path] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rd := range txFrom {
|
||||||
|
if _, ok := reads[rd.Path]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildDAG(deps TxnInputOutput) (d DAG) {
|
||||||
|
d = DAG{dag.NewDAG()}
|
||||||
|
ids := make(map[int]string)
|
||||||
|
|
||||||
|
for i := len(deps.inputs) - 1; i > 0; i-- {
|
||||||
|
txTo := deps.inputs[i]
|
||||||
|
|
||||||
|
var txToId string
|
||||||
|
|
||||||
|
if _, ok := ids[i]; ok {
|
||||||
|
txToId = ids[i]
|
||||||
|
} else {
|
||||||
|
txToId, _ = d.AddVertex(i)
|
||||||
|
ids[i] = txToId
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := i - 1; j >= 0; j-- {
|
||||||
|
txFrom := deps.allOutputs[j]
|
||||||
|
|
||||||
|
if HasReadDep(txFrom, txTo) {
|
||||||
|
var txFromId string
|
||||||
|
if _, ok := ids[j]; ok {
|
||||||
|
txFromId = ids[j]
|
||||||
|
} else {
|
||||||
|
txFromId, _ = d.AddVertex(j)
|
||||||
|
ids[j] = txFromId
|
||||||
|
}
|
||||||
|
|
||||||
|
err := d.AddEdge(txFromId, txToId)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to add edge", "from", txFromId, "to", txToId, "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func depsHelper(dependencies map[int]map[int]bool, txFrom TxnOutput, txTo TxnInput, i int, j int) map[int]map[int]bool {
|
||||||
|
if HasReadDep(txFrom, txTo) {
|
||||||
|
dependencies[i][j] = true
|
||||||
|
|
||||||
|
for k := range dependencies[i] {
|
||||||
|
_, foundDep := dependencies[j][k]
|
||||||
|
|
||||||
|
if foundDep {
|
||||||
|
delete(dependencies[i], k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dependencies
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateDeps(deps map[int]map[int]bool, t TxDep) map[int]map[int]bool {
|
||||||
|
txTo := t.ReadList
|
||||||
|
|
||||||
|
deps[t.Index] = map[int]bool{}
|
||||||
|
|
||||||
|
for j := 0; j <= t.Index-1; j++ {
|
||||||
|
txFrom := t.FullWriteList[j]
|
||||||
|
|
||||||
|
deps = depsHelper(deps, txFrom, txTo, t.Index, j)
|
||||||
|
}
|
||||||
|
|
||||||
|
return deps
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDep(deps TxnInputOutput) map[int]map[int]bool {
|
||||||
|
newDependencies := map[int]map[int]bool{}
|
||||||
|
|
||||||
|
for i := 1; i < len(deps.inputs); i++ {
|
||||||
|
txTo := deps.inputs[i]
|
||||||
|
|
||||||
|
newDependencies[i] = map[int]bool{}
|
||||||
|
|
||||||
|
for j := 0; j <= i-1; j++ {
|
||||||
|
txFrom := deps.allOutputs[j]
|
||||||
|
|
||||||
|
newDependencies = depsHelper(newDependencies, txFrom, txTo, i, j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newDependencies
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the longest execution path in the DAG
|
||||||
|
func (d DAG) LongestPath(stats map[int]ExecutionStat) ([]int, uint64) {
|
||||||
|
prev := make(map[int]int, len(d.GetVertices()))
|
||||||
|
|
||||||
|
for i := 0; i < len(d.GetVertices()); i++ {
|
||||||
|
prev[i] = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
pathWeights := make(map[int]uint64, len(d.GetVertices()))
|
||||||
|
|
||||||
|
maxPath := 0
|
||||||
|
maxPathWeight := uint64(0)
|
||||||
|
|
||||||
|
idxToId := make(map[int]string, len(d.GetVertices()))
|
||||||
|
|
||||||
|
for k, i := range d.GetVertices() {
|
||||||
|
idxToId[i.(int)] = k
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(idxToId); i++ {
|
||||||
|
parents, _ := d.GetParents(idxToId[i])
|
||||||
|
|
||||||
|
if len(parents) > 0 {
|
||||||
|
for _, p := range parents {
|
||||||
|
weight := pathWeights[p.(int)] + stats[i].End - stats[i].Start
|
||||||
|
if weight > pathWeights[i] {
|
||||||
|
pathWeights[i] = weight
|
||||||
|
prev[i] = p.(int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pathWeights[i] = stats[i].End - stats[i].Start
|
||||||
|
}
|
||||||
|
|
||||||
|
if pathWeights[i] > maxPathWeight {
|
||||||
|
maxPath = i
|
||||||
|
maxPathWeight = pathWeights[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
path := make([]int, 0)
|
||||||
|
for i := maxPath; i != -1; i = prev[i] {
|
||||||
|
path = append(path, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse the path so the transactions are in the ascending order
|
||||||
|
for i, j := 0, len(path)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
path[i], path[j] = path[j], path[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return path, maxPathWeight
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d DAG) Report(stats map[int]ExecutionStat, out func(string)) {
|
||||||
|
longestPath, weight := d.LongestPath(stats)
|
||||||
|
|
||||||
|
serialWeight := uint64(0)
|
||||||
|
|
||||||
|
for i := 0; i < len(d.GetVertices()); i++ {
|
||||||
|
serialWeight += stats[i].End - stats[i].Start
|
||||||
|
}
|
||||||
|
|
||||||
|
makeStrs := func(ints []int) (ret []string) {
|
||||||
|
for _, v := range ints {
|
||||||
|
ret = append(ret, fmt.Sprint(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
out("Longest execution path:")
|
||||||
|
out(fmt.Sprintf("(%v) %v", len(longestPath), strings.Join(makeStrs(longestPath), "->")))
|
||||||
|
|
||||||
|
out(fmt.Sprintf("Longest path ideal execution time: %v of %v (serial total), %v%%", time.Duration(weight),
|
||||||
|
time.Duration(serialWeight), fmt.Sprintf("%.1f", float64(weight)*100.0/float64(serialWeight))))
|
||||||
|
}
|
||||||
641
core/blockstm/executor.go
Normal file
641
core/blockstm/executor.go
Normal file
|
|
@ -0,0 +1,641 @@
|
||||||
|
package blockstm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/heap"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExecResult struct {
|
||||||
|
err error
|
||||||
|
ver Version
|
||||||
|
txIn TxnInput
|
||||||
|
txOut TxnOutput
|
||||||
|
txAllOut TxnOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExecTask interface {
|
||||||
|
Execute(mvh *MVHashMap, incarnation int) error
|
||||||
|
MVReadList() []ReadDescriptor
|
||||||
|
MVWriteList() []WriteDescriptor
|
||||||
|
MVFullWriteList() []WriteDescriptor
|
||||||
|
Hash() common.Hash
|
||||||
|
Sender() common.Address
|
||||||
|
Settle()
|
||||||
|
Dependencies() []int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExecVersionView struct {
|
||||||
|
ver Version
|
||||||
|
et ExecTask
|
||||||
|
mvh *MVHashMap
|
||||||
|
sender common.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
var NumSpeculativeProcs int = 8
|
||||||
|
|
||||||
|
func SetProcs(specProcs int) {
|
||||||
|
NumSpeculativeProcs = specProcs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ev *ExecVersionView) Execute() (er ExecResult) {
|
||||||
|
er.ver = ev.ver
|
||||||
|
if er.err = ev.et.Execute(ev.mvh, ev.ver.Incarnation); er.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
er.txIn = ev.et.MVReadList()
|
||||||
|
er.txOut = ev.et.MVWriteList()
|
||||||
|
er.txAllOut = ev.et.MVFullWriteList()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrExecAbortError struct {
|
||||||
|
Dependency int
|
||||||
|
OriginError error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrExecAbortError) Error() string {
|
||||||
|
if e.Dependency >= 0 {
|
||||||
|
return fmt.Sprintf("Execution aborted due to dependency %d", e.Dependency)
|
||||||
|
} else {
|
||||||
|
return "Execution aborted"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParallelExecFailedError struct {
|
||||||
|
Msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ParallelExecFailedError) Error() string {
|
||||||
|
return e.Msg
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntHeap []int
|
||||||
|
|
||||||
|
func (h IntHeap) Len() int { return len(h) }
|
||||||
|
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
|
||||||
|
func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||||
|
|
||||||
|
func (h *IntHeap) Push(x any) {
|
||||||
|
// Push and Pop use pointer receivers because they modify the slice's length,
|
||||||
|
// not just its contents.
|
||||||
|
*h = append(*h, x.(int))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *IntHeap) Pop() any {
|
||||||
|
old := *h
|
||||||
|
n := len(old)
|
||||||
|
x := old[n-1]
|
||||||
|
*h = old[0 : n-1]
|
||||||
|
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
type SafeQueue interface {
|
||||||
|
Push(v int, d interface{})
|
||||||
|
Pop() interface{}
|
||||||
|
Len() int
|
||||||
|
}
|
||||||
|
|
||||||
|
type SafeFIFOQueue struct {
|
||||||
|
c chan interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSafeFIFOQueue(capacity int) *SafeFIFOQueue {
|
||||||
|
return &SafeFIFOQueue{
|
||||||
|
c: make(chan interface{}, capacity),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *SafeFIFOQueue) Push(v int, d interface{}) {
|
||||||
|
q.c <- d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *SafeFIFOQueue) Pop() interface{} {
|
||||||
|
return <-q.c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *SafeFIFOQueue) Len() int {
|
||||||
|
return len(q.c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A thread safe priority queue
|
||||||
|
type SafePriorityQueue struct {
|
||||||
|
m sync.Mutex
|
||||||
|
queue *IntHeap
|
||||||
|
data map[int]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSafePriorityQueue(capacity int) *SafePriorityQueue {
|
||||||
|
q := make(IntHeap, 0, capacity)
|
||||||
|
|
||||||
|
return &SafePriorityQueue{
|
||||||
|
m: sync.Mutex{},
|
||||||
|
queue: &q,
|
||||||
|
data: make(map[int]interface{}, capacity),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pq *SafePriorityQueue) Push(v int, d interface{}) {
|
||||||
|
pq.m.Lock()
|
||||||
|
|
||||||
|
heap.Push(pq.queue, v)
|
||||||
|
pq.data[v] = d
|
||||||
|
|
||||||
|
pq.m.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pq *SafePriorityQueue) Pop() interface{} {
|
||||||
|
pq.m.Lock()
|
||||||
|
defer pq.m.Unlock()
|
||||||
|
|
||||||
|
v := heap.Pop(pq.queue).(int)
|
||||||
|
|
||||||
|
return pq.data[v]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pq *SafePriorityQueue) Len() int {
|
||||||
|
return pq.queue.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParallelExecutionResult struct {
|
||||||
|
TxIO *TxnInputOutput
|
||||||
|
Stats *map[int]ExecutionStat
|
||||||
|
Deps *DAG
|
||||||
|
AllDeps map[int]map[int]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
const numGoProcs = 1
|
||||||
|
|
||||||
|
type ParallelExecutor struct {
|
||||||
|
tasks []ExecTask
|
||||||
|
|
||||||
|
// Stores the execution statistics for the last incarnation of each task
|
||||||
|
stats map[int]ExecutionStat
|
||||||
|
|
||||||
|
statsMutex sync.Mutex
|
||||||
|
|
||||||
|
// Channel for tasks that should be prioritized
|
||||||
|
chTasks chan ExecVersionView
|
||||||
|
|
||||||
|
// Channel for speculative tasks
|
||||||
|
chSpeculativeTasks chan struct{}
|
||||||
|
|
||||||
|
// Channel to signal that the result of a transaction could be written to storage
|
||||||
|
specTaskQueue SafeQueue
|
||||||
|
|
||||||
|
// A priority queue that stores speculative tasks
|
||||||
|
chSettle chan int
|
||||||
|
|
||||||
|
// Channel to signal that a transaction has finished executing
|
||||||
|
chResults chan struct{}
|
||||||
|
|
||||||
|
// A priority queue that stores the transaction index of results, so we can validate the results in order
|
||||||
|
resultQueue SafeQueue
|
||||||
|
|
||||||
|
// A wait group to wait for all settling tasks to finish
|
||||||
|
settleWg sync.WaitGroup
|
||||||
|
|
||||||
|
// An integer that tracks the index of last settled transaction
|
||||||
|
lastSettled int
|
||||||
|
|
||||||
|
// For a task that runs only after all of its preceding tasks have finished and passed validation,
|
||||||
|
// its result will be absolutely valid and therefore its validation could be skipped.
|
||||||
|
// This map stores the boolean value indicating whether a task satisfy this condition ( absolutely valid).
|
||||||
|
skipCheck map[int]bool
|
||||||
|
|
||||||
|
// Execution tasks stores the state of each execution task
|
||||||
|
execTasks taskStatusManager
|
||||||
|
|
||||||
|
// Validate tasks stores the state of each validation task
|
||||||
|
validateTasks taskStatusManager
|
||||||
|
|
||||||
|
// Stats for debugging purposes
|
||||||
|
cntExec, cntSuccess, cntAbort, cntTotalValidations, cntValidationFail int
|
||||||
|
|
||||||
|
diagExecSuccess, diagExecAbort []int
|
||||||
|
|
||||||
|
// Multi-version hash map
|
||||||
|
mvh *MVHashMap
|
||||||
|
|
||||||
|
// Stores the inputs and outputs of the last incardanotion of all transactions
|
||||||
|
lastTxIO *TxnInputOutput
|
||||||
|
|
||||||
|
// Tracks the incarnation number of each transaction
|
||||||
|
txIncarnations []int
|
||||||
|
|
||||||
|
// A map that stores the estimated dependency of a transaction if it is aborted without any known dependency
|
||||||
|
estimateDeps map[int][]int
|
||||||
|
|
||||||
|
// A map that records whether a transaction result has been speculatively validated
|
||||||
|
preValidated map[int]bool
|
||||||
|
|
||||||
|
// Time records when the parallel execution starts
|
||||||
|
begin time.Time
|
||||||
|
|
||||||
|
// Enable profiling
|
||||||
|
profile bool
|
||||||
|
|
||||||
|
// Worker wait group
|
||||||
|
workerWg sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExecutionStat struct {
|
||||||
|
TxIdx int
|
||||||
|
Incarnation int
|
||||||
|
Start uint64
|
||||||
|
End uint64
|
||||||
|
Worker int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewParallelExecutor(tasks []ExecTask, profile bool, metadata bool) *ParallelExecutor {
|
||||||
|
numTasks := len(tasks)
|
||||||
|
|
||||||
|
var resultQueue SafeQueue
|
||||||
|
|
||||||
|
var specTaskQueue SafeQueue
|
||||||
|
|
||||||
|
if metadata {
|
||||||
|
resultQueue = NewSafeFIFOQueue(numTasks)
|
||||||
|
specTaskQueue = NewSafeFIFOQueue(numTasks)
|
||||||
|
} else {
|
||||||
|
resultQueue = NewSafePriorityQueue(numTasks)
|
||||||
|
specTaskQueue = NewSafePriorityQueue(numTasks)
|
||||||
|
}
|
||||||
|
|
||||||
|
pe := &ParallelExecutor{
|
||||||
|
tasks: tasks,
|
||||||
|
stats: make(map[int]ExecutionStat, numTasks),
|
||||||
|
chTasks: make(chan ExecVersionView, numTasks),
|
||||||
|
chSpeculativeTasks: make(chan struct{}, numTasks),
|
||||||
|
chSettle: make(chan int, numTasks),
|
||||||
|
chResults: make(chan struct{}, numTasks),
|
||||||
|
specTaskQueue: specTaskQueue,
|
||||||
|
resultQueue: resultQueue,
|
||||||
|
lastSettled: -1,
|
||||||
|
skipCheck: make(map[int]bool),
|
||||||
|
execTasks: makeStatusManager(numTasks),
|
||||||
|
validateTasks: makeStatusManager(0),
|
||||||
|
diagExecSuccess: make([]int, numTasks),
|
||||||
|
diagExecAbort: make([]int, numTasks),
|
||||||
|
mvh: MakeMVHashMap(),
|
||||||
|
lastTxIO: MakeTxnInputOutput(numTasks),
|
||||||
|
txIncarnations: make([]int, numTasks),
|
||||||
|
estimateDeps: make(map[int][]int),
|
||||||
|
preValidated: make(map[int]bool),
|
||||||
|
begin: time.Now(),
|
||||||
|
profile: profile,
|
||||||
|
}
|
||||||
|
|
||||||
|
return pe
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint: gocognit
|
||||||
|
func (pe *ParallelExecutor) Prepare() error {
|
||||||
|
prevSenderTx := make(map[common.Address]int)
|
||||||
|
|
||||||
|
for i, t := range pe.tasks {
|
||||||
|
clearPendingFlag := false
|
||||||
|
|
||||||
|
pe.skipCheck[i] = false
|
||||||
|
pe.estimateDeps[i] = make([]int, 0)
|
||||||
|
|
||||||
|
if len(t.Dependencies()) > 0 {
|
||||||
|
for _, val := range t.Dependencies() {
|
||||||
|
clearPendingFlag = true
|
||||||
|
|
||||||
|
pe.execTasks.addDependencies(val, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if clearPendingFlag {
|
||||||
|
pe.execTasks.clearPending(i)
|
||||||
|
|
||||||
|
clearPendingFlag = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if tx, ok := prevSenderTx[t.Sender()]; ok {
|
||||||
|
pe.execTasks.addDependencies(tx, i)
|
||||||
|
pe.execTasks.clearPending(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
prevSenderTx[t.Sender()] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pe.workerWg.Add(NumSpeculativeProcs + numGoProcs)
|
||||||
|
|
||||||
|
// Launch workers that execute transactions
|
||||||
|
for i := 0; i < NumSpeculativeProcs+numGoProcs; i++ {
|
||||||
|
go func(procNum int) {
|
||||||
|
defer pe.workerWg.Done()
|
||||||
|
|
||||||
|
doWork := func(task ExecVersionView) {
|
||||||
|
start := time.Duration(0)
|
||||||
|
if pe.profile {
|
||||||
|
start = time.Since(pe.begin)
|
||||||
|
}
|
||||||
|
|
||||||
|
res := task.Execute()
|
||||||
|
|
||||||
|
if res.err == nil {
|
||||||
|
pe.mvh.FlushMVWriteSet(res.txAllOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
pe.resultQueue.Push(res.ver.TxnIndex, res)
|
||||||
|
pe.chResults <- struct{}{}
|
||||||
|
|
||||||
|
if pe.profile {
|
||||||
|
end := time.Since(pe.begin)
|
||||||
|
|
||||||
|
pe.statsMutex.Lock()
|
||||||
|
pe.stats[res.ver.TxnIndex] = ExecutionStat{
|
||||||
|
TxIdx: res.ver.TxnIndex,
|
||||||
|
Incarnation: res.ver.Incarnation,
|
||||||
|
Start: uint64(start),
|
||||||
|
End: uint64(end),
|
||||||
|
Worker: procNum,
|
||||||
|
}
|
||||||
|
pe.statsMutex.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if procNum < NumSpeculativeProcs {
|
||||||
|
for range pe.chSpeculativeTasks {
|
||||||
|
doWork(pe.specTaskQueue.Pop().(ExecVersionView))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for task := range pe.chTasks {
|
||||||
|
doWork(task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
pe.settleWg.Add(1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for t := range pe.chSettle {
|
||||||
|
pe.tasks[t].Settle()
|
||||||
|
}
|
||||||
|
|
||||||
|
pe.settleWg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// bootstrap first execution
|
||||||
|
tx := pe.execTasks.takeNextPending()
|
||||||
|
|
||||||
|
if tx == -1 {
|
||||||
|
return ParallelExecFailedError{"no executable transactions due to bad dependency"}
|
||||||
|
}
|
||||||
|
|
||||||
|
pe.cntExec++
|
||||||
|
|
||||||
|
pe.chTasks <- ExecVersionView{ver: Version{tx, 0}, et: pe.tasks[tx], mvh: pe.mvh, sender: pe.tasks[tx].Sender()}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pe *ParallelExecutor) Close(wait bool) {
|
||||||
|
close(pe.chTasks)
|
||||||
|
close(pe.chSpeculativeTasks)
|
||||||
|
close(pe.chSettle)
|
||||||
|
|
||||||
|
if wait {
|
||||||
|
pe.workerWg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
if wait {
|
||||||
|
pe.settleWg.Wait()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint: gocognit
|
||||||
|
func (pe *ParallelExecutor) Step(res *ExecResult) (result ParallelExecutionResult, err error) {
|
||||||
|
tx := res.ver.TxnIndex
|
||||||
|
|
||||||
|
if abortErr, ok := res.err.(ErrExecAbortError); ok && abortErr.OriginError != nil && pe.skipCheck[tx] {
|
||||||
|
// If the transaction failed when we know it should not fail, this means the transaction itself is
|
||||||
|
// bad (e.g. wrong nonce), and we should exit the execution immediately
|
||||||
|
err = fmt.Errorf("could not apply tx %d [%v]: %w", tx, pe.tasks[tx].Hash(), abortErr.OriginError)
|
||||||
|
pe.Close(true)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint: nestif
|
||||||
|
if execErr, ok := res.err.(ErrExecAbortError); ok {
|
||||||
|
addedDependencies := false
|
||||||
|
|
||||||
|
if execErr.Dependency >= 0 {
|
||||||
|
l := len(pe.estimateDeps[tx])
|
||||||
|
for l > 0 && pe.estimateDeps[tx][l-1] > execErr.Dependency {
|
||||||
|
pe.execTasks.removeDependency(pe.estimateDeps[tx][l-1])
|
||||||
|
pe.estimateDeps[tx] = pe.estimateDeps[tx][:l-1]
|
||||||
|
l--
|
||||||
|
}
|
||||||
|
|
||||||
|
addedDependencies = pe.execTasks.addDependencies(execErr.Dependency, tx)
|
||||||
|
} else {
|
||||||
|
estimate := 0
|
||||||
|
|
||||||
|
if len(pe.estimateDeps[tx]) > 0 {
|
||||||
|
estimate = pe.estimateDeps[tx][len(pe.estimateDeps[tx])-1]
|
||||||
|
}
|
||||||
|
addedDependencies = pe.execTasks.addDependencies(estimate, tx)
|
||||||
|
newEstimate := estimate + (estimate+tx)/2
|
||||||
|
if newEstimate >= tx {
|
||||||
|
newEstimate = tx - 1
|
||||||
|
}
|
||||||
|
pe.estimateDeps[tx] = append(pe.estimateDeps[tx], newEstimate)
|
||||||
|
}
|
||||||
|
|
||||||
|
pe.execTasks.clearInProgress(tx)
|
||||||
|
|
||||||
|
if !addedDependencies {
|
||||||
|
pe.execTasks.pushPending(tx)
|
||||||
|
}
|
||||||
|
pe.txIncarnations[tx]++
|
||||||
|
pe.diagExecAbort[tx]++
|
||||||
|
pe.cntAbort++
|
||||||
|
} else {
|
||||||
|
pe.lastTxIO.recordRead(tx, res.txIn)
|
||||||
|
|
||||||
|
if res.ver.Incarnation == 0 {
|
||||||
|
pe.lastTxIO.recordWrite(tx, res.txOut)
|
||||||
|
pe.lastTxIO.recordAllWrite(tx, res.txAllOut)
|
||||||
|
} else {
|
||||||
|
if res.txAllOut.hasNewWrite(pe.lastTxIO.AllWriteSet(tx)) {
|
||||||
|
pe.validateTasks.pushPendingSet(pe.execTasks.getRevalidationRange(tx + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
prevWrite := pe.lastTxIO.AllWriteSet(tx)
|
||||||
|
|
||||||
|
// Remove entries that were previously written but are no longer written
|
||||||
|
|
||||||
|
cmpMap := make(map[Key]bool)
|
||||||
|
|
||||||
|
for _, w := range res.txAllOut {
|
||||||
|
cmpMap[w.Path] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range prevWrite {
|
||||||
|
if _, ok := cmpMap[v.Path]; !ok {
|
||||||
|
pe.mvh.Delete(v.Path, tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pe.lastTxIO.recordWrite(tx, res.txOut)
|
||||||
|
pe.lastTxIO.recordAllWrite(tx, res.txAllOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
pe.validateTasks.pushPending(tx)
|
||||||
|
pe.execTasks.markComplete(tx)
|
||||||
|
pe.diagExecSuccess[tx]++
|
||||||
|
pe.cntSuccess++
|
||||||
|
|
||||||
|
pe.execTasks.removeDependency(tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// do validations ...
|
||||||
|
maxComplete := pe.execTasks.maxAllComplete()
|
||||||
|
|
||||||
|
toValidate := make([]int, 0, 2)
|
||||||
|
|
||||||
|
for pe.validateTasks.minPending() <= maxComplete && pe.validateTasks.minPending() >= 0 {
|
||||||
|
toValidate = append(toValidate, pe.validateTasks.takeNextPending())
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(toValidate); i++ {
|
||||||
|
pe.cntTotalValidations++
|
||||||
|
|
||||||
|
tx := toValidate[i]
|
||||||
|
|
||||||
|
if pe.skipCheck[tx] || ValidateVersion(tx, pe.lastTxIO, pe.mvh) {
|
||||||
|
pe.validateTasks.markComplete(tx)
|
||||||
|
} else {
|
||||||
|
pe.cntValidationFail++
|
||||||
|
pe.diagExecAbort[tx]++
|
||||||
|
for _, v := range pe.lastTxIO.AllWriteSet(tx) {
|
||||||
|
pe.mvh.MarkEstimate(v.Path, tx)
|
||||||
|
}
|
||||||
|
// 'create validation tasks for all transactions > tx ...'
|
||||||
|
pe.validateTasks.pushPendingSet(pe.execTasks.getRevalidationRange(tx + 1))
|
||||||
|
pe.validateTasks.clearInProgress(tx) // clear in progress - pending will be added again once new incarnation executes
|
||||||
|
|
||||||
|
pe.execTasks.clearComplete(tx)
|
||||||
|
pe.execTasks.pushPending(tx)
|
||||||
|
|
||||||
|
pe.preValidated[tx] = false
|
||||||
|
pe.txIncarnations[tx]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Settle transactions that have been validated to be correct and that won't be re-executed again
|
||||||
|
maxValidated := pe.validateTasks.maxAllComplete()
|
||||||
|
|
||||||
|
for pe.lastSettled < maxValidated {
|
||||||
|
pe.lastSettled++
|
||||||
|
if pe.execTasks.checkInProgress(pe.lastSettled) || pe.execTasks.checkPending(pe.lastSettled) || pe.execTasks.isBlocked(pe.lastSettled) {
|
||||||
|
pe.lastSettled--
|
||||||
|
break
|
||||||
|
}
|
||||||
|
pe.chSettle <- pe.lastSettled
|
||||||
|
}
|
||||||
|
|
||||||
|
if pe.validateTasks.countComplete() == len(pe.tasks) && pe.execTasks.countComplete() == len(pe.tasks) {
|
||||||
|
log.Debug("blockstm exec summary", "execs", pe.cntExec, "success", pe.cntSuccess, "aborts", pe.cntAbort, "validations", pe.cntTotalValidations, "failures", pe.cntValidationFail, "#tasks/#execs", fmt.Sprintf("%.2f%%", float64(len(pe.tasks))/float64(pe.cntExec)*100))
|
||||||
|
|
||||||
|
pe.Close(true)
|
||||||
|
|
||||||
|
var allDeps map[int]map[int]bool
|
||||||
|
|
||||||
|
var deps DAG
|
||||||
|
|
||||||
|
if pe.profile {
|
||||||
|
allDeps = GetDep(*pe.lastTxIO)
|
||||||
|
deps = BuildDAG(*pe.lastTxIO)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParallelExecutionResult{pe.lastTxIO, &pe.stats, &deps, allDeps}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the next immediate pending transaction to be executed
|
||||||
|
if pe.execTasks.minPending() != -1 && pe.execTasks.minPending() == maxValidated+1 {
|
||||||
|
nextTx := pe.execTasks.takeNextPending()
|
||||||
|
if nextTx != -1 {
|
||||||
|
pe.cntExec++
|
||||||
|
|
||||||
|
pe.skipCheck[nextTx] = true
|
||||||
|
|
||||||
|
pe.chTasks <- ExecVersionView{ver: Version{nextTx, pe.txIncarnations[nextTx]}, et: pe.tasks[nextTx], mvh: pe.mvh, sender: pe.tasks[nextTx].Sender()}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send speculative tasks
|
||||||
|
for pe.execTasks.minPending() != -1 {
|
||||||
|
nextTx := pe.execTasks.takeNextPending()
|
||||||
|
|
||||||
|
if nextTx != -1 {
|
||||||
|
pe.cntExec++
|
||||||
|
|
||||||
|
task := ExecVersionView{ver: Version{nextTx, pe.txIncarnations[nextTx]}, et: pe.tasks[nextTx], mvh: pe.mvh, sender: pe.tasks[nextTx].Sender()}
|
||||||
|
|
||||||
|
pe.specTaskQueue.Push(nextTx, task)
|
||||||
|
pe.chSpeculativeTasks <- struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type PropertyCheck func(*ParallelExecutor) error
|
||||||
|
|
||||||
|
func executeParallelWithCheck(tasks []ExecTask, profile bool, check PropertyCheck, metadata bool, interruptCtx context.Context) (result ParallelExecutionResult, err error) {
|
||||||
|
if len(tasks) == 0 {
|
||||||
|
return ParallelExecutionResult{MakeTxnInputOutput(len(tasks)), nil, nil, nil}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pe := NewParallelExecutor(tasks, profile, metadata)
|
||||||
|
err = pe.Prepare()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
pe.Close(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for range pe.chResults {
|
||||||
|
if interruptCtx != nil && interruptCtx.Err() != nil {
|
||||||
|
pe.Close(true)
|
||||||
|
return result, interruptCtx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
res := pe.resultQueue.Pop().(ExecResult)
|
||||||
|
|
||||||
|
result, err = pe.Step(&res)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if check != nil {
|
||||||
|
err = check(pe)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.TxIO != nil || err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecuteParallel(tasks []ExecTask, profile bool, metadata bool, interruptCtx context.Context) (result ParallelExecutionResult, err error) {
|
||||||
|
return executeParallelWithCheck(tasks, profile, nil, metadata, interruptCtx)
|
||||||
|
}
|
||||||
984
core/blockstm/executor_test.go
Normal file
984
core/blockstm/executor_test.go
Normal file
|
|
@ -0,0 +1,984 @@
|
||||||
|
package blockstm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OpType int
|
||||||
|
|
||||||
|
const readType = 0
|
||||||
|
const writeType = 1
|
||||||
|
const otherType = 2
|
||||||
|
const greenTick = "✅"
|
||||||
|
const redCross = "❌"
|
||||||
|
|
||||||
|
const threeRockets = "🚀🚀🚀"
|
||||||
|
|
||||||
|
type Op struct {
|
||||||
|
key Key
|
||||||
|
duration time.Duration
|
||||||
|
opType OpType
|
||||||
|
val int
|
||||||
|
}
|
||||||
|
|
||||||
|
type testExecTask struct {
|
||||||
|
txIdx int
|
||||||
|
ops []Op
|
||||||
|
readMap map[Key]ReadDescriptor
|
||||||
|
writeMap map[Key]WriteDescriptor
|
||||||
|
sender common.Address
|
||||||
|
nonce int
|
||||||
|
dependencies []int
|
||||||
|
}
|
||||||
|
|
||||||
|
type PathGenerator func(addr common.Address, i int, j int, total int) Key
|
||||||
|
|
||||||
|
type TaskRunner func(numTx int, numRead int, numWrite int, numNonIO int) (time.Duration, time.Duration)
|
||||||
|
|
||||||
|
type TaskRunnerWithMetadata func(numTx int, numRead int, numWrite int, numNonIO int) (time.Duration, time.Duration, time.Duration)
|
||||||
|
|
||||||
|
type Timer func(txIdx int, opIdx int) time.Duration
|
||||||
|
|
||||||
|
type Sender func(int) common.Address
|
||||||
|
|
||||||
|
func NewTestExecTask(txIdx int, ops []Op, sender common.Address, nonce int) *testExecTask {
|
||||||
|
return &testExecTask{
|
||||||
|
txIdx: txIdx,
|
||||||
|
ops: ops,
|
||||||
|
readMap: make(map[Key]ReadDescriptor),
|
||||||
|
writeMap: make(map[Key]WriteDescriptor),
|
||||||
|
sender: sender,
|
||||||
|
nonce: nonce,
|
||||||
|
dependencies: []int{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sleep(i time.Duration) {
|
||||||
|
start := time.Now()
|
||||||
|
for time.Since(start) < i {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testExecTask) Execute(mvh *MVHashMap, incarnation int) error {
|
||||||
|
// Sleep for 50 microsecond to simulate setup time
|
||||||
|
sleep(time.Microsecond * 50)
|
||||||
|
|
||||||
|
version := Version{TxnIndex: t.txIdx, Incarnation: incarnation}
|
||||||
|
|
||||||
|
t.readMap = make(map[Key]ReadDescriptor)
|
||||||
|
t.writeMap = make(map[Key]WriteDescriptor)
|
||||||
|
|
||||||
|
deps := -1
|
||||||
|
|
||||||
|
for i, op := range t.ops {
|
||||||
|
k := op.key
|
||||||
|
|
||||||
|
switch op.opType {
|
||||||
|
case readType:
|
||||||
|
if _, ok := t.writeMap[k]; ok {
|
||||||
|
sleep(op.duration)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
result := mvh.Read(k, t.txIdx)
|
||||||
|
|
||||||
|
val := result.Value()
|
||||||
|
|
||||||
|
if i == 0 && val != nil && (val.(int) != t.nonce) {
|
||||||
|
return ErrExecAbortError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.Status() == MVReadResultDependency {
|
||||||
|
if result.depIdx > deps {
|
||||||
|
deps = result.depIdx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var readKind int
|
||||||
|
|
||||||
|
if result.Status() == MVReadResultDone {
|
||||||
|
readKind = ReadKindMap
|
||||||
|
} else if result.Status() == MVReadResultNone {
|
||||||
|
readKind = ReadKindStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(op.duration)
|
||||||
|
|
||||||
|
t.readMap[k] = ReadDescriptor{k, readKind, Version{TxnIndex: result.depIdx, Incarnation: result.incarnation}}
|
||||||
|
case writeType:
|
||||||
|
t.writeMap[k] = WriteDescriptor{k, version, op.val}
|
||||||
|
case otherType:
|
||||||
|
sleep(op.duration)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unknown op type: %d", op.opType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if deps != -1 {
|
||||||
|
return ErrExecAbortError{deps, fmt.Errorf("Dependency error")}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testExecTask) MVWriteList() []WriteDescriptor {
|
||||||
|
return t.MVFullWriteList()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testExecTask) MVFullWriteList() []WriteDescriptor {
|
||||||
|
writes := make([]WriteDescriptor, 0, len(t.writeMap))
|
||||||
|
|
||||||
|
for _, v := range t.writeMap {
|
||||||
|
writes = append(writes, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return writes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testExecTask) MVReadList() []ReadDescriptor {
|
||||||
|
reads := make([]ReadDescriptor, 0, len(t.readMap))
|
||||||
|
|
||||||
|
for _, v := range t.readMap {
|
||||||
|
reads = append(reads, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return reads
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testExecTask) Settle() {}
|
||||||
|
|
||||||
|
func (t *testExecTask) Sender() common.Address {
|
||||||
|
return t.sender
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testExecTask) Hash() common.Hash {
|
||||||
|
return common.BytesToHash([]byte(fmt.Sprintf("%d", t.txIdx)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testExecTask) Dependencies() []int {
|
||||||
|
return t.dependencies
|
||||||
|
}
|
||||||
|
|
||||||
|
func randTimeGenerator(min time.Duration, max time.Duration) func(txIdx int, opIdx int) time.Duration {
|
||||||
|
return func(txIdx int, opIdx int) time.Duration {
|
||||||
|
return time.Duration(rand.Int63n(int64(max-min))) + min
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func longTailTimeGenerator(min time.Duration, max time.Duration, i int, j int) func(txIdx int, opIdx int) time.Duration {
|
||||||
|
return func(txIdx int, opIdx int) time.Duration {
|
||||||
|
if txIdx%i == 0 && opIdx == j {
|
||||||
|
return max * 100
|
||||||
|
} else {
|
||||||
|
return time.Duration(rand.Int63n(int64(max-min))) + min
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var randomPathGenerator = func(sender common.Address, i int, j int, total int) Key {
|
||||||
|
return NewStateKey(common.BigToAddress((big.NewInt(int64(i % 10)))), common.BigToHash((big.NewInt(int64(total)))))
|
||||||
|
}
|
||||||
|
|
||||||
|
var dexPathGenerator = func(sender common.Address, i int, j int, total int) Key {
|
||||||
|
if j == total-1 || j == 2 {
|
||||||
|
return NewSubpathKey(common.BigToAddress(big.NewInt(int64(0))), 1)
|
||||||
|
} else {
|
||||||
|
return NewSubpathKey(common.BigToAddress(big.NewInt(int64(j))), 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var readTime = randTimeGenerator(4*time.Microsecond, 12*time.Microsecond)
|
||||||
|
var writeTime = randTimeGenerator(2*time.Microsecond, 6*time.Microsecond)
|
||||||
|
var nonIOTime = randTimeGenerator(1*time.Microsecond, 2*time.Microsecond)
|
||||||
|
|
||||||
|
func taskFactory(numTask int, sender Sender, readsPerT int, writesPerT int, nonIOPerT int, pathGenerator PathGenerator, readTime Timer, writeTime Timer, nonIOTime Timer) ([]ExecTask, time.Duration) {
|
||||||
|
exec := make([]ExecTask, 0, numTask)
|
||||||
|
|
||||||
|
var serialDuration time.Duration
|
||||||
|
|
||||||
|
senderNonces := make(map[common.Address]int)
|
||||||
|
|
||||||
|
for i := 0; i < numTask; i++ {
|
||||||
|
s := sender(i)
|
||||||
|
|
||||||
|
// Set first two ops to always read and write nonce
|
||||||
|
ops := make([]Op, 0, readsPerT+writesPerT+nonIOPerT)
|
||||||
|
|
||||||
|
ops = append(ops, Op{opType: readType, key: NewSubpathKey(s, 2), duration: readTime(i, 0), val: senderNonces[s]})
|
||||||
|
|
||||||
|
senderNonces[s]++
|
||||||
|
|
||||||
|
ops = append(ops, Op{opType: writeType, key: NewSubpathKey(s, 2), duration: writeTime(i, 1), val: senderNonces[s]})
|
||||||
|
|
||||||
|
for j := 0; j < readsPerT-1; j++ {
|
||||||
|
ops = append(ops, Op{opType: readType})
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < nonIOPerT; j++ {
|
||||||
|
ops = append(ops, Op{opType: otherType})
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < writesPerT-1; j++ {
|
||||||
|
ops = append(ops, Op{opType: writeType})
|
||||||
|
}
|
||||||
|
|
||||||
|
// shuffle ops except for the first three (read nonce, write nonce, another read) ops and last write op.
|
||||||
|
// This enables random path generator to generate deterministic paths for these "special" ops.
|
||||||
|
for j := 3; j < len(ops)-1; j++ {
|
||||||
|
k := rand.Intn(len(ops)-j-1) + j
|
||||||
|
ops[j], ops[k] = ops[k], ops[j]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate time and key path for each op except first two that are always read and write nonce
|
||||||
|
for j := 2; j < len(ops); j++ {
|
||||||
|
if ops[j].opType == readType {
|
||||||
|
ops[j].key = pathGenerator(s, i, j, len(ops))
|
||||||
|
ops[j].duration = readTime(i, j)
|
||||||
|
} else if ops[j].opType == writeType {
|
||||||
|
ops[j].key = pathGenerator(s, i, j, len(ops))
|
||||||
|
ops[j].duration = writeTime(i, j)
|
||||||
|
} else {
|
||||||
|
ops[j].duration = nonIOTime(i, j)
|
||||||
|
}
|
||||||
|
|
||||||
|
serialDuration += ops[j].duration
|
||||||
|
}
|
||||||
|
|
||||||
|
if ops[len(ops)-1].opType != writeType {
|
||||||
|
panic("Last op must be a write")
|
||||||
|
}
|
||||||
|
|
||||||
|
t := NewTestExecTask(i, ops, s, senderNonces[s]-1)
|
||||||
|
exec = append(exec, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
return exec, serialDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
func testExecutorComb(t *testing.T, totalTxs []int, numReads []int, numWrites []int, numNonIO []int, taskRunner TaskRunner) {
|
||||||
|
t.Helper()
|
||||||
|
log.Root().SetHandler(log.LvlFilterHandler(log.LvlDebug, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||||
|
|
||||||
|
improved := 0
|
||||||
|
total := 0
|
||||||
|
|
||||||
|
totalExecDuration := time.Duration(0)
|
||||||
|
totalSerialDuration := time.Duration(0)
|
||||||
|
|
||||||
|
for _, numTx := range totalTxs {
|
||||||
|
for _, numRead := range numReads {
|
||||||
|
for _, numWrite := range numWrites {
|
||||||
|
for _, numNonIO := range numNonIO {
|
||||||
|
log.Info("Executing block", "numTx", numTx, "numRead", numRead, "numWrite", numWrite, "numNonIO", numNonIO)
|
||||||
|
execDuration, expectedSerialDuration := taskRunner(numTx, numRead, numWrite, numNonIO)
|
||||||
|
|
||||||
|
if execDuration < expectedSerialDuration {
|
||||||
|
improved++
|
||||||
|
}
|
||||||
|
total++
|
||||||
|
|
||||||
|
performance := greenTick
|
||||||
|
|
||||||
|
if execDuration >= expectedSerialDuration {
|
||||||
|
performance = redCross
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("exec duration %v, serial duration %v, time reduced %v %.2f%%, %v \n", execDuration, expectedSerialDuration, expectedSerialDuration-execDuration, float64(expectedSerialDuration-execDuration)/float64(expectedSerialDuration)*100, performance)
|
||||||
|
|
||||||
|
totalExecDuration += execDuration
|
||||||
|
totalSerialDuration += expectedSerialDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Improved: ", improved, "Total: ", total, "success rate: ", float64(improved)/float64(total)*100)
|
||||||
|
fmt.Printf("Total exec duration: %v, total serial duration: %v, time reduced: %v, time reduced percent: %.2f%%\n", totalExecDuration, totalSerialDuration, totalSerialDuration-totalExecDuration, float64(totalSerialDuration-totalExecDuration)/float64(totalSerialDuration)*100)
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint: gocognit
|
||||||
|
func testExecutorCombWithMetadata(t *testing.T, totalTxs []int, numReads []int, numWrites []int, numNonIOs []int, taskRunner TaskRunnerWithMetadata) {
|
||||||
|
t.Helper()
|
||||||
|
log.Root().SetHandler(log.LvlFilterHandler(log.LvlDebug, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||||
|
|
||||||
|
improved := 0
|
||||||
|
improvedMetadata := 0
|
||||||
|
rocket := 0
|
||||||
|
total := 0
|
||||||
|
|
||||||
|
totalExecDuration := time.Duration(0)
|
||||||
|
totalExecDurationMetadata := time.Duration(0)
|
||||||
|
totalSerialDuration := time.Duration(0)
|
||||||
|
|
||||||
|
for _, numTx := range totalTxs {
|
||||||
|
for _, numRead := range numReads {
|
||||||
|
for _, numWrite := range numWrites {
|
||||||
|
for _, numNonIO := range numNonIOs {
|
||||||
|
log.Info("Executing block", "numTx", numTx, "numRead", numRead, "numWrite", numWrite, "numNonIO", numNonIO)
|
||||||
|
execDuration, execDurationMetadata, expectedSerialDuration := taskRunner(numTx, numRead, numWrite, numNonIO)
|
||||||
|
|
||||||
|
if execDuration < expectedSerialDuration {
|
||||||
|
improved++
|
||||||
|
}
|
||||||
|
total++
|
||||||
|
|
||||||
|
performance := greenTick
|
||||||
|
|
||||||
|
if execDuration >= expectedSerialDuration {
|
||||||
|
performance = redCross
|
||||||
|
|
||||||
|
if execDurationMetadata <= expectedSerialDuration {
|
||||||
|
performance = threeRockets
|
||||||
|
rocket++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if execDuration >= execDurationMetadata {
|
||||||
|
improvedMetadata++
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("WITHOUT METADATA: exec duration %v, serial duration %v, time reduced %v %.2f%%, %v \n", execDuration, expectedSerialDuration, expectedSerialDuration-execDuration, float64(expectedSerialDuration-execDuration)/float64(expectedSerialDuration)*100, performance)
|
||||||
|
fmt.Printf("WITH METADATA: exec duration %v, exec duration with metadata %v, time reduced %v %.2f%%\n", execDuration, execDurationMetadata, execDuration-execDurationMetadata, float64(execDuration-execDurationMetadata)/float64(execDuration)*100)
|
||||||
|
|
||||||
|
totalExecDuration += execDuration
|
||||||
|
totalExecDurationMetadata += execDurationMetadata
|
||||||
|
totalSerialDuration += expectedSerialDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\nImproved: ", improved, "Total: ", total, "success rate: ", float64(improved)/float64(total)*100)
|
||||||
|
fmt.Println("Metadata Better: ", improvedMetadata, "out of: ", total, "success rate: ", float64(improvedMetadata)/float64(total)*100)
|
||||||
|
fmt.Println("Rockets (Time of: metadata < serial < without metadata): ", rocket)
|
||||||
|
fmt.Printf("\nWithout metadata <> serial: Total exec duration: %v, total serial duration : %v, time reduced: %v, time reduced percent: %.2f%%\n", totalExecDuration, totalSerialDuration, totalSerialDuration-totalExecDuration, float64(totalSerialDuration-totalExecDuration)/float64(totalSerialDuration)*100)
|
||||||
|
fmt.Printf("With metadata <> serial: Total exec duration metadata: %v, total serial duration : %v, time reduced: %v, time reduced percent: %.2f%%\n", totalExecDurationMetadata, totalSerialDuration, totalSerialDuration-totalExecDurationMetadata, float64(totalSerialDuration-totalExecDurationMetadata)/float64(totalSerialDuration)*100)
|
||||||
|
fmt.Printf("Without metadata <> with metadata: Total exec duration: %v, total exec duration metadata: %v, time reduced: %v, time reduced percent: %.2f%%\n", totalExecDuration, totalExecDurationMetadata, totalExecDuration-totalExecDurationMetadata, float64(totalExecDuration-totalExecDurationMetadata)/float64(totalExecDuration)*100)
|
||||||
|
}
|
||||||
|
|
||||||
|
func composeValidations(checks []PropertyCheck) PropertyCheck {
|
||||||
|
return func(pe *ParallelExecutor) error {
|
||||||
|
for _, check := range checks {
|
||||||
|
err := check(pe)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkNoStatusOverlap(pe *ParallelExecutor) error {
|
||||||
|
seen := make(map[int]string)
|
||||||
|
|
||||||
|
for _, tx := range pe.execTasks.complete {
|
||||||
|
seen[tx] = "complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tx := range pe.execTasks.inProgress {
|
||||||
|
if v, ok := seen[tx]; ok {
|
||||||
|
return fmt.Errorf("tx %v is in both %v and inProgress", v, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
seen[tx] = "inProgress"
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tx := range pe.execTasks.pending {
|
||||||
|
if v, ok := seen[tx]; ok {
|
||||||
|
return fmt.Errorf("tx %v is in both %v complete and pending", v, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
seen[tx] = "pending"
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkNoDroppedTx(pe *ParallelExecutor) error {
|
||||||
|
for i := 0; i < len(pe.tasks); i++ {
|
||||||
|
if !pe.execTasks.checkComplete(i) && !pe.execTasks.checkInProgress(i) && !pe.execTasks.checkPending(i) {
|
||||||
|
if !pe.execTasks.isBlocked(i) {
|
||||||
|
return fmt.Errorf("tx %v is not in any status and is not blocked by any other tx", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint: unparam
|
||||||
|
func runParallel(t *testing.T, tasks []ExecTask, validation PropertyCheck, metadata bool) time.Duration {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
profile := false
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
result, err := executeParallelWithCheck(tasks, false, validation, metadata, nil)
|
||||||
|
|
||||||
|
if result.Deps != nil && profile {
|
||||||
|
result.Deps.Report(*result.Stats, func(str string) { fmt.Println(str) })
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NoError(t, err, "error occur during parallel execution")
|
||||||
|
|
||||||
|
// Need to apply the final write set to storage
|
||||||
|
|
||||||
|
finalWriteSet := make(map[Key]time.Duration)
|
||||||
|
|
||||||
|
for _, task := range tasks {
|
||||||
|
task := task.(*testExecTask)
|
||||||
|
for _, op := range task.ops {
|
||||||
|
if op.opType == writeType {
|
||||||
|
finalWriteSet[op.key] = op.duration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range finalWriteSet {
|
||||||
|
sleep(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
duration := time.Since(start)
|
||||||
|
|
||||||
|
return duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func runParallelGetMetadata(t *testing.T, tasks []ExecTask, validation PropertyCheck) map[int]map[int]bool {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
res, err := executeParallelWithCheck(tasks, true, validation, false, nil)
|
||||||
|
|
||||||
|
assert.NoError(t, err, "error occur during parallel execution")
|
||||||
|
|
||||||
|
return res.AllDeps
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessConflicts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
rand.Seed(0)
|
||||||
|
|
||||||
|
totalTxs := []int{10, 50, 100, 200, 300}
|
||||||
|
numReads := []int{20, 100, 200}
|
||||||
|
numWrites := []int{20, 100, 200}
|
||||||
|
numNonIO := []int{100, 500}
|
||||||
|
|
||||||
|
checks := composeValidations([]PropertyCheck{checkNoStatusOverlap, checkNoDroppedTx})
|
||||||
|
|
||||||
|
taskRunner := func(numTx int, numRead int, numWrite int, numNonIO int) (time.Duration, time.Duration) {
|
||||||
|
sender := func(i int) common.Address {
|
||||||
|
randomness := rand.Intn(10) + 10
|
||||||
|
return common.BigToAddress(big.NewInt(int64(i % randomness)))
|
||||||
|
}
|
||||||
|
tasks, serialDuration := taskFactory(numTx, sender, numRead, numWrite, numNonIO, randomPathGenerator, readTime, writeTime, nonIOTime)
|
||||||
|
|
||||||
|
return runParallel(t, tasks, checks, false), serialDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
testExecutorComb(t, totalTxs, numReads, numWrites, numNonIO, taskRunner)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessConflictsWithMetadata(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
rand.Seed(0)
|
||||||
|
|
||||||
|
totalTxs := []int{300}
|
||||||
|
numReads := []int{100, 200}
|
||||||
|
numWrites := []int{100, 200}
|
||||||
|
numNonIOs := []int{100, 500}
|
||||||
|
|
||||||
|
checks := composeValidations([]PropertyCheck{checkNoStatusOverlap, checkNoDroppedTx})
|
||||||
|
|
||||||
|
taskRunner := func(numTx int, numRead int, numWrite int, numNonIO int) (time.Duration, time.Duration, time.Duration) {
|
||||||
|
sender := func(i int) common.Address {
|
||||||
|
randomness := rand.Intn(10) + 10
|
||||||
|
return common.BigToAddress(big.NewInt(int64(i % randomness)))
|
||||||
|
}
|
||||||
|
tasks, serialDuration := taskFactory(numTx, sender, numRead, numWrite, numNonIO, randomPathGenerator, readTime, writeTime, nonIOTime)
|
||||||
|
|
||||||
|
parallelDuration := runParallel(t, tasks, checks, false)
|
||||||
|
|
||||||
|
allDeps := runParallelGetMetadata(t, tasks, checks)
|
||||||
|
|
||||||
|
newTasks := make([]ExecTask, 0, len(tasks))
|
||||||
|
|
||||||
|
for _, t := range tasks {
|
||||||
|
temp := t.(*testExecTask)
|
||||||
|
|
||||||
|
keys := make([]int, len(allDeps[temp.txIdx]))
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
|
||||||
|
for k := range allDeps[temp.txIdx] {
|
||||||
|
keys[i] = k
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
temp.dependencies = keys
|
||||||
|
newTasks = append(newTasks, temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parallelDuration, runParallel(t, newTasks, checks, true), serialDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
testExecutorCombWithMetadata(t, totalTxs, numReads, numWrites, numNonIOs, taskRunner)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZeroTx(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
rand.Seed(0)
|
||||||
|
|
||||||
|
totalTxs := []int{0}
|
||||||
|
numReads := []int{20}
|
||||||
|
numWrites := []int{20}
|
||||||
|
numNonIO := []int{100}
|
||||||
|
|
||||||
|
checks := composeValidations([]PropertyCheck{checkNoStatusOverlap, checkNoDroppedTx})
|
||||||
|
|
||||||
|
taskRunner := func(numTx int, numRead int, numWrite int, numNonIO int) (time.Duration, time.Duration) {
|
||||||
|
sender := func(i int) common.Address { return common.BigToAddress(big.NewInt(int64(1))) }
|
||||||
|
tasks, serialDuration := taskFactory(numTx, sender, numRead, numWrite, numNonIO, randomPathGenerator, readTime, writeTime, nonIOTime)
|
||||||
|
|
||||||
|
return runParallel(t, tasks, checks, false), serialDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
testExecutorComb(t, totalTxs, numReads, numWrites, numNonIO, taskRunner)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAlternatingTx(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
rand.Seed(0)
|
||||||
|
|
||||||
|
totalTxs := []int{200}
|
||||||
|
numReads := []int{20}
|
||||||
|
numWrites := []int{20}
|
||||||
|
numNonIO := []int{100}
|
||||||
|
|
||||||
|
checks := composeValidations([]PropertyCheck{checkNoStatusOverlap, checkNoDroppedTx})
|
||||||
|
|
||||||
|
taskRunner := func(numTx int, numRead int, numWrite int, numNonIO int) (time.Duration, time.Duration) {
|
||||||
|
sender := func(i int) common.Address { return common.BigToAddress(big.NewInt(int64(i % 2))) }
|
||||||
|
tasks, serialDuration := taskFactory(numTx, sender, numRead, numWrite, numNonIO, randomPathGenerator, readTime, writeTime, nonIOTime)
|
||||||
|
|
||||||
|
return runParallel(t, tasks, checks, false), serialDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
testExecutorComb(t, totalTxs, numReads, numWrites, numNonIO, taskRunner)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAlternatingTxWithMetadata(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
rand.Seed(0)
|
||||||
|
|
||||||
|
totalTxs := []int{200}
|
||||||
|
numReads := []int{20}
|
||||||
|
numWrites := []int{20}
|
||||||
|
numNonIO := []int{100}
|
||||||
|
|
||||||
|
checks := composeValidations([]PropertyCheck{checkNoStatusOverlap, checkNoDroppedTx})
|
||||||
|
|
||||||
|
taskRunner := func(numTx int, numRead int, numWrite int, numNonIO int) (time.Duration, time.Duration, time.Duration) {
|
||||||
|
sender := func(i int) common.Address { return common.BigToAddress(big.NewInt(int64(i % 2))) }
|
||||||
|
tasks, serialDuration := taskFactory(numTx, sender, numRead, numWrite, numNonIO, randomPathGenerator, readTime, writeTime, nonIOTime)
|
||||||
|
|
||||||
|
parallelDuration := runParallel(t, tasks, checks, false)
|
||||||
|
|
||||||
|
allDeps := runParallelGetMetadata(t, tasks, checks)
|
||||||
|
|
||||||
|
newTasks := make([]ExecTask, 0, len(tasks))
|
||||||
|
|
||||||
|
for _, t := range tasks {
|
||||||
|
temp := t.(*testExecTask)
|
||||||
|
|
||||||
|
keys := make([]int, len(allDeps[temp.txIdx]))
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
|
||||||
|
for k := range allDeps[temp.txIdx] {
|
||||||
|
keys[i] = k
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
temp.dependencies = keys
|
||||||
|
newTasks = append(newTasks, temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parallelDuration, runParallel(t, newTasks, checks, true), serialDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
testExecutorCombWithMetadata(t, totalTxs, numReads, numWrites, numNonIO, taskRunner)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMoreConflicts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
rand.Seed(0)
|
||||||
|
|
||||||
|
totalTxs := []int{10, 50, 100, 200, 300}
|
||||||
|
numReads := []int{20, 100, 200}
|
||||||
|
numWrites := []int{20, 100, 200}
|
||||||
|
numNonIO := []int{100, 500}
|
||||||
|
|
||||||
|
checks := composeValidations([]PropertyCheck{checkNoStatusOverlap, checkNoDroppedTx})
|
||||||
|
|
||||||
|
taskRunner := func(numTx int, numRead int, numWrite int, numNonIO int) (time.Duration, time.Duration) {
|
||||||
|
sender := func(i int) common.Address {
|
||||||
|
randomness := rand.Intn(10) + 10
|
||||||
|
return common.BigToAddress(big.NewInt(int64(i / randomness)))
|
||||||
|
}
|
||||||
|
tasks, serialDuration := taskFactory(numTx, sender, numRead, numWrite, numNonIO, randomPathGenerator, readTime, writeTime, nonIOTime)
|
||||||
|
|
||||||
|
return runParallel(t, tasks, checks, false), serialDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
testExecutorComb(t, totalTxs, numReads, numWrites, numNonIO, taskRunner)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMoreConflictsWithMetadata(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
rand.Seed(0)
|
||||||
|
|
||||||
|
totalTxs := []int{300}
|
||||||
|
numReads := []int{100, 200}
|
||||||
|
numWrites := []int{100, 200}
|
||||||
|
numNonIO := []int{100, 500}
|
||||||
|
|
||||||
|
checks := composeValidations([]PropertyCheck{checkNoStatusOverlap, checkNoDroppedTx})
|
||||||
|
|
||||||
|
taskRunner := func(numTx int, numRead int, numWrite int, numNonIO int) (time.Duration, time.Duration, time.Duration) {
|
||||||
|
sender := func(i int) common.Address {
|
||||||
|
randomness := rand.Intn(10) + 10
|
||||||
|
return common.BigToAddress(big.NewInt(int64(i / randomness)))
|
||||||
|
}
|
||||||
|
tasks, serialDuration := taskFactory(numTx, sender, numRead, numWrite, numNonIO, randomPathGenerator, readTime, writeTime, nonIOTime)
|
||||||
|
|
||||||
|
parallelDuration := runParallel(t, tasks, checks, false)
|
||||||
|
|
||||||
|
allDeps := runParallelGetMetadata(t, tasks, checks)
|
||||||
|
|
||||||
|
newTasks := make([]ExecTask, 0, len(tasks))
|
||||||
|
|
||||||
|
for _, t := range tasks {
|
||||||
|
temp := t.(*testExecTask)
|
||||||
|
|
||||||
|
keys := make([]int, len(allDeps[temp.txIdx]))
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
|
||||||
|
for k := range allDeps[temp.txIdx] {
|
||||||
|
keys[i] = k
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
temp.dependencies = keys
|
||||||
|
newTasks = append(newTasks, temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parallelDuration, runParallel(t, newTasks, checks, true), serialDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
testExecutorCombWithMetadata(t, totalTxs, numReads, numWrites, numNonIO, taskRunner)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRandomTx(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
rand.Seed(0)
|
||||||
|
|
||||||
|
totalTxs := []int{10, 50, 100, 200, 300}
|
||||||
|
numReads := []int{20, 100, 200}
|
||||||
|
numWrites := []int{20, 100, 200}
|
||||||
|
numNonIO := []int{100, 500}
|
||||||
|
|
||||||
|
checks := composeValidations([]PropertyCheck{checkNoStatusOverlap, checkNoDroppedTx})
|
||||||
|
|
||||||
|
taskRunner := func(numTx int, numRead int, numWrite int, numNonIO int) (time.Duration, time.Duration) {
|
||||||
|
// Randomly assign this tx to one of 10 senders
|
||||||
|
sender := func(i int) common.Address { return common.BigToAddress(big.NewInt(int64(rand.Intn(10)))) }
|
||||||
|
tasks, serialDuration := taskFactory(numTx, sender, numRead, numWrite, numNonIO, randomPathGenerator, readTime, writeTime, nonIOTime)
|
||||||
|
|
||||||
|
return runParallel(t, tasks, checks, false), serialDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
testExecutorComb(t, totalTxs, numReads, numWrites, numNonIO, taskRunner)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRandomTxWithMetadata(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
rand.Seed(0)
|
||||||
|
|
||||||
|
totalTxs := []int{300}
|
||||||
|
numReads := []int{100, 200}
|
||||||
|
numWrites := []int{100, 200}
|
||||||
|
numNonIO := []int{100, 500}
|
||||||
|
|
||||||
|
checks := composeValidations([]PropertyCheck{checkNoStatusOverlap, checkNoDroppedTx})
|
||||||
|
|
||||||
|
taskRunner := func(numTx int, numRead int, numWrite int, numNonIO int) (time.Duration, time.Duration, time.Duration) {
|
||||||
|
// Randomly assign this tx to one of 10 senders
|
||||||
|
sender := func(i int) common.Address { return common.BigToAddress(big.NewInt(int64(rand.Intn(10)))) }
|
||||||
|
tasks, serialDuration := taskFactory(numTx, sender, numRead, numWrite, numNonIO, randomPathGenerator, readTime, writeTime, nonIOTime)
|
||||||
|
|
||||||
|
parallelDuration := runParallel(t, tasks, checks, false)
|
||||||
|
|
||||||
|
allDeps := runParallelGetMetadata(t, tasks, checks)
|
||||||
|
|
||||||
|
newTasks := make([]ExecTask, 0, len(tasks))
|
||||||
|
|
||||||
|
for _, t := range tasks {
|
||||||
|
temp := t.(*testExecTask)
|
||||||
|
|
||||||
|
keys := make([]int, len(allDeps[temp.txIdx]))
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
|
||||||
|
for k := range allDeps[temp.txIdx] {
|
||||||
|
keys[i] = k
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
temp.dependencies = keys
|
||||||
|
newTasks = append(newTasks, temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parallelDuration, runParallel(t, newTasks, checks, true), serialDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
testExecutorCombWithMetadata(t, totalTxs, numReads, numWrites, numNonIO, taskRunner)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxWithLongTailRead(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
rand.Seed(0)
|
||||||
|
|
||||||
|
totalTxs := []int{10, 50, 100, 200, 300}
|
||||||
|
numReads := []int{20, 100, 200}
|
||||||
|
numWrites := []int{20, 100, 200}
|
||||||
|
numNonIO := []int{100, 500}
|
||||||
|
|
||||||
|
checks := composeValidations([]PropertyCheck{checkNoStatusOverlap, checkNoDroppedTx})
|
||||||
|
|
||||||
|
taskRunner := func(numTx int, numRead int, numWrite int, numNonIO int) (time.Duration, time.Duration) {
|
||||||
|
sender := func(i int) common.Address {
|
||||||
|
randomness := rand.Intn(10) + 10
|
||||||
|
return common.BigToAddress(big.NewInt(int64(i / randomness)))
|
||||||
|
}
|
||||||
|
|
||||||
|
longTailReadTimer := longTailTimeGenerator(4*time.Microsecond, 12*time.Microsecond, 7, 10)
|
||||||
|
|
||||||
|
tasks, serialDuration := taskFactory(numTx, sender, numRead, numWrite, numNonIO, randomPathGenerator, longTailReadTimer, writeTime, nonIOTime)
|
||||||
|
|
||||||
|
return runParallel(t, tasks, checks, false), serialDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
testExecutorComb(t, totalTxs, numReads, numWrites, numNonIO, taskRunner)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxWithLongTailReadWithMetadata(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
rand.Seed(0)
|
||||||
|
|
||||||
|
totalTxs := []int{300}
|
||||||
|
numReads := []int{100, 200}
|
||||||
|
numWrites := []int{100, 200}
|
||||||
|
numNonIO := []int{100, 500}
|
||||||
|
|
||||||
|
checks := composeValidations([]PropertyCheck{checkNoStatusOverlap, checkNoDroppedTx})
|
||||||
|
|
||||||
|
taskRunner := func(numTx int, numRead int, numWrite int, numNonIO int) (time.Duration, time.Duration, time.Duration) {
|
||||||
|
sender := func(i int) common.Address {
|
||||||
|
randomness := rand.Intn(10) + 10
|
||||||
|
return common.BigToAddress(big.NewInt(int64(i / randomness)))
|
||||||
|
}
|
||||||
|
|
||||||
|
longTailReadTimer := longTailTimeGenerator(4*time.Microsecond, 12*time.Microsecond, 7, 10)
|
||||||
|
|
||||||
|
tasks, serialDuration := taskFactory(numTx, sender, numRead, numWrite, numNonIO, randomPathGenerator, longTailReadTimer, writeTime, nonIOTime)
|
||||||
|
|
||||||
|
parallelDuration := runParallel(t, tasks, checks, false)
|
||||||
|
|
||||||
|
allDeps := runParallelGetMetadata(t, tasks, checks)
|
||||||
|
|
||||||
|
newTasks := make([]ExecTask, 0, len(tasks))
|
||||||
|
|
||||||
|
for _, t := range tasks {
|
||||||
|
temp := t.(*testExecTask)
|
||||||
|
|
||||||
|
keys := make([]int, len(allDeps[temp.txIdx]))
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
|
||||||
|
for k := range allDeps[temp.txIdx] {
|
||||||
|
keys[i] = k
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
temp.dependencies = keys
|
||||||
|
newTasks = append(newTasks, temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parallelDuration, runParallel(t, newTasks, checks, true), serialDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
testExecutorCombWithMetadata(t, totalTxs, numReads, numWrites, numNonIO, taskRunner)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDexScenario(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
rand.Seed(0)
|
||||||
|
|
||||||
|
totalTxs := []int{10, 50, 100, 200, 300}
|
||||||
|
numReads := []int{20, 100, 200}
|
||||||
|
numWrites := []int{20, 100, 200}
|
||||||
|
numNonIO := []int{100, 500}
|
||||||
|
|
||||||
|
postValidation := func(pe *ParallelExecutor) error {
|
||||||
|
if pe.lastSettled == len(pe.tasks) {
|
||||||
|
for i, inputs := range pe.lastTxIO.inputs {
|
||||||
|
for _, input := range inputs {
|
||||||
|
if input.V.TxnIndex != i-1 {
|
||||||
|
return fmt.Errorf("Tx %d should depend on tx %d, but it actually depends on %d", i, i-1, input.V.TxnIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
checks := composeValidations([]PropertyCheck{checkNoStatusOverlap, postValidation, checkNoDroppedTx})
|
||||||
|
|
||||||
|
taskRunner := func(numTx int, numRead int, numWrite int, numNonIO int) (time.Duration, time.Duration) {
|
||||||
|
sender := func(i int) common.Address { return common.BigToAddress(big.NewInt(int64(i))) }
|
||||||
|
tasks, serialDuration := taskFactory(numTx, sender, numRead, numWrite, numNonIO, dexPathGenerator, readTime, writeTime, nonIOTime)
|
||||||
|
|
||||||
|
return runParallel(t, tasks, checks, false), serialDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
testExecutorComb(t, totalTxs, numReads, numWrites, numNonIO, taskRunner)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDexScenarioWithMetadata(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
rand.Seed(0)
|
||||||
|
|
||||||
|
totalTxs := []int{300}
|
||||||
|
numReads := []int{100, 200}
|
||||||
|
numWrites := []int{100, 200}
|
||||||
|
numNonIO := []int{100, 500}
|
||||||
|
|
||||||
|
postValidation := func(pe *ParallelExecutor) error {
|
||||||
|
if pe.lastSettled == len(pe.tasks) {
|
||||||
|
for i, inputs := range pe.lastTxIO.inputs {
|
||||||
|
for _, input := range inputs {
|
||||||
|
if input.V.TxnIndex != i-1 {
|
||||||
|
return fmt.Errorf("Tx %d should depend on tx %d, but it actually depends on %d", i, i-1, input.V.TxnIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
checks := composeValidations([]PropertyCheck{checkNoStatusOverlap, postValidation, checkNoDroppedTx})
|
||||||
|
|
||||||
|
taskRunner := func(numTx int, numRead int, numWrite int, numNonIO int) (time.Duration, time.Duration, time.Duration) {
|
||||||
|
sender := func(i int) common.Address { return common.BigToAddress(big.NewInt(int64(i))) }
|
||||||
|
tasks, serialDuration := taskFactory(numTx, sender, numRead, numWrite, numNonIO, dexPathGenerator, readTime, writeTime, nonIOTime)
|
||||||
|
|
||||||
|
parallelDuration := runParallel(t, tasks, checks, false)
|
||||||
|
|
||||||
|
allDeps := runParallelGetMetadata(t, tasks, checks)
|
||||||
|
|
||||||
|
newTasks := make([]ExecTask, 0, len(tasks))
|
||||||
|
|
||||||
|
for _, t := range tasks {
|
||||||
|
temp := t.(*testExecTask)
|
||||||
|
|
||||||
|
keys := make([]int, len(allDeps[temp.txIdx]))
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
|
||||||
|
for k := range allDeps[temp.txIdx] {
|
||||||
|
keys[i] = k
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
temp.dependencies = keys
|
||||||
|
newTasks = append(newTasks, temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parallelDuration, runParallel(t, newTasks, checks, true), serialDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
testExecutorCombWithMetadata(t, totalTxs, numReads, numWrites, numNonIO, taskRunner)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBreakFromCircularDependency(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
rand.Seed(0)
|
||||||
|
|
||||||
|
tasks := make([]ExecTask, 5)
|
||||||
|
|
||||||
|
for i := range tasks {
|
||||||
|
tasks[i] = &testExecTask{
|
||||||
|
txIdx: i,
|
||||||
|
dependencies: []int{
|
||||||
|
(i + len(tasks) - 1) % len(tasks),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
// This should not hang
|
||||||
|
_, err := ExecuteParallel(tasks, false, true, ctx)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected cancel error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBreakFromPartialCircularDependency(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
rand.Seed(0)
|
||||||
|
|
||||||
|
tasks := make([]ExecTask, 5)
|
||||||
|
|
||||||
|
for i := range tasks {
|
||||||
|
if i < 3 {
|
||||||
|
tasks[i] = &testExecTask{
|
||||||
|
txIdx: i,
|
||||||
|
dependencies: []int{
|
||||||
|
(i + 2) % 3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tasks[i] = &testExecTask{
|
||||||
|
txIdx: i,
|
||||||
|
dependencies: []int{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
// This should not hang
|
||||||
|
_, err := ExecuteParallel(tasks, false, true, ctx)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected cancel error")
|
||||||
|
}
|
||||||
|
}
|
||||||
290
core/blockstm/mvhashmap.go
Normal file
290
core/blockstm/mvhashmap.go
Normal file
|
|
@ -0,0 +1,290 @@
|
||||||
|
package blockstm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/emirpasic/gods/maps/treemap"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
const FlagDone = 0
|
||||||
|
const FlagEstimate = 1
|
||||||
|
|
||||||
|
const addressType = 1
|
||||||
|
const stateType = 2
|
||||||
|
const subpathType = 3
|
||||||
|
|
||||||
|
const KeyLength = common.AddressLength + common.HashLength + 2
|
||||||
|
|
||||||
|
type Key [KeyLength]byte
|
||||||
|
|
||||||
|
func (k Key) IsAddress() bool {
|
||||||
|
return k[KeyLength-1] == addressType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Key) IsState() bool {
|
||||||
|
return k[KeyLength-1] == stateType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Key) IsSubpath() bool {
|
||||||
|
return k[KeyLength-1] == subpathType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Key) GetAddress() common.Address {
|
||||||
|
return common.BytesToAddress(k[:common.AddressLength])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Key) GetStateKey() common.Hash {
|
||||||
|
return common.BytesToHash(k[common.AddressLength : KeyLength-2])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Key) GetSubpath() byte {
|
||||||
|
return k[KeyLength-2]
|
||||||
|
}
|
||||||
|
|
||||||
|
func newKey(addr common.Address, hash common.Hash, subpath byte, keyType byte) Key {
|
||||||
|
var k Key
|
||||||
|
|
||||||
|
copy(k[:common.AddressLength], addr.Bytes())
|
||||||
|
copy(k[common.AddressLength:KeyLength-2], hash.Bytes())
|
||||||
|
k[KeyLength-2] = subpath
|
||||||
|
k[KeyLength-1] = keyType
|
||||||
|
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAddressKey(addr common.Address) Key {
|
||||||
|
return newKey(addr, common.Hash{}, 0, addressType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStateKey(addr common.Address, hash common.Hash) Key {
|
||||||
|
k := newKey(addr, hash, 0, stateType)
|
||||||
|
if !k.IsState() {
|
||||||
|
panic(fmt.Errorf("key is not a state key"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSubpathKey(addr common.Address, subpath byte) Key {
|
||||||
|
return newKey(addr, common.Hash{}, subpath, subpathType)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MVHashMap struct {
|
||||||
|
m sync.Map
|
||||||
|
s sync.Map
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeMVHashMap() *MVHashMap {
|
||||||
|
return &MVHashMap{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type WriteCell struct {
|
||||||
|
flag uint
|
||||||
|
incarnation int
|
||||||
|
data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TxnIndexCells struct {
|
||||||
|
rw sync.RWMutex
|
||||||
|
tm *treemap.Map
|
||||||
|
}
|
||||||
|
|
||||||
|
type Version struct {
|
||||||
|
TxnIndex int
|
||||||
|
Incarnation int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mv *MVHashMap) getKeyCells(k Key, fNoKey func(kenc Key) *TxnIndexCells) (cells *TxnIndexCells) {
|
||||||
|
val, ok := mv.m.Load(k)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
cells = fNoKey(k)
|
||||||
|
} else {
|
||||||
|
cells = val.(*TxnIndexCells)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mv *MVHashMap) Write(k Key, v Version, data interface{}) {
|
||||||
|
cells := mv.getKeyCells(k, func(kenc Key) (cells *TxnIndexCells) {
|
||||||
|
n := &TxnIndexCells{
|
||||||
|
rw: sync.RWMutex{},
|
||||||
|
tm: treemap.NewWithIntComparator(),
|
||||||
|
}
|
||||||
|
cells = n
|
||||||
|
val, _ := mv.m.LoadOrStore(kenc, n)
|
||||||
|
cells = val.(*TxnIndexCells)
|
||||||
|
return
|
||||||
|
})
|
||||||
|
|
||||||
|
cells.rw.RLock()
|
||||||
|
ci, ok := cells.tm.Get(v.TxnIndex)
|
||||||
|
cells.rw.RUnlock()
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
if ci.(*WriteCell).incarnation > v.Incarnation {
|
||||||
|
panic(fmt.Errorf("existing transaction value does not have lower incarnation: %v, %v",
|
||||||
|
k, v.TxnIndex))
|
||||||
|
}
|
||||||
|
|
||||||
|
ci.(*WriteCell).flag = FlagDone
|
||||||
|
ci.(*WriteCell).incarnation = v.Incarnation
|
||||||
|
ci.(*WriteCell).data = data
|
||||||
|
} else {
|
||||||
|
cells.rw.Lock()
|
||||||
|
if ci, ok = cells.tm.Get(v.TxnIndex); !ok {
|
||||||
|
cells.tm.Put(v.TxnIndex, &WriteCell{
|
||||||
|
flag: FlagDone,
|
||||||
|
incarnation: v.Incarnation,
|
||||||
|
data: data,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ci.(*WriteCell).flag = FlagDone
|
||||||
|
ci.(*WriteCell).incarnation = v.Incarnation
|
||||||
|
ci.(*WriteCell).data = data
|
||||||
|
}
|
||||||
|
cells.rw.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mv *MVHashMap) ReadStorage(k Key, fallBack func() any) any {
|
||||||
|
data, ok := mv.s.Load(string(k[:]))
|
||||||
|
if !ok {
|
||||||
|
data = fallBack()
|
||||||
|
data, _ = mv.s.LoadOrStore(string(k[:]), data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mv *MVHashMap) MarkEstimate(k Key, txIdx int) {
|
||||||
|
cells := mv.getKeyCells(k, func(_ Key) *TxnIndexCells {
|
||||||
|
panic(fmt.Errorf("path must already exist"))
|
||||||
|
})
|
||||||
|
|
||||||
|
cells.rw.RLock()
|
||||||
|
if ci, ok := cells.tm.Get(txIdx); !ok {
|
||||||
|
panic(fmt.Sprintf("should not happen - cell should be present for path. TxIdx: %v, path, %x, cells keys: %v", txIdx, k, cells.tm.Keys()))
|
||||||
|
} else {
|
||||||
|
ci.(*WriteCell).flag = FlagEstimate
|
||||||
|
}
|
||||||
|
cells.rw.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mv *MVHashMap) Delete(k Key, txIdx int) {
|
||||||
|
cells := mv.getKeyCells(k, func(_ Key) *TxnIndexCells {
|
||||||
|
panic(fmt.Errorf("path must already exist"))
|
||||||
|
})
|
||||||
|
|
||||||
|
cells.rw.Lock()
|
||||||
|
defer cells.rw.Unlock()
|
||||||
|
cells.tm.Remove(txIdx)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
MVReadResultDone = 0
|
||||||
|
MVReadResultDependency = 1
|
||||||
|
MVReadResultNone = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
type MVReadResult struct {
|
||||||
|
depIdx int
|
||||||
|
incarnation int
|
||||||
|
value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res *MVReadResult) DepIdx() int {
|
||||||
|
return res.depIdx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res *MVReadResult) Incarnation() int {
|
||||||
|
return res.incarnation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res *MVReadResult) Value() interface{} {
|
||||||
|
return res.value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mvr MVReadResult) Status() int {
|
||||||
|
if mvr.depIdx != -1 {
|
||||||
|
if mvr.incarnation == -1 {
|
||||||
|
return MVReadResultDependency
|
||||||
|
} else {
|
||||||
|
return MVReadResultDone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MVReadResultNone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mv *MVHashMap) Read(k Key, txIdx int) (res MVReadResult) {
|
||||||
|
res.depIdx = -1
|
||||||
|
res.incarnation = -1
|
||||||
|
|
||||||
|
cells := mv.getKeyCells(k, func(_ Key) *TxnIndexCells {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if cells == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cells.rw.RLock()
|
||||||
|
fk, fv := cells.tm.Floor(txIdx - 1)
|
||||||
|
cells.rw.RUnlock()
|
||||||
|
|
||||||
|
if fk != nil && fv != nil {
|
||||||
|
c := fv.(*WriteCell)
|
||||||
|
switch c.flag {
|
||||||
|
case FlagEstimate:
|
||||||
|
res.depIdx = fk.(int)
|
||||||
|
res.value = c.data
|
||||||
|
case FlagDone:
|
||||||
|
{
|
||||||
|
res.depIdx = fk.(int)
|
||||||
|
res.incarnation = c.incarnation
|
||||||
|
res.value = c.data
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("should not happen - unknown flag value"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mv *MVHashMap) FlushMVWriteSet(writes []WriteDescriptor) {
|
||||||
|
for _, v := range writes {
|
||||||
|
mv.Write(v.Path, v.V, v.Val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateVersion(txIdx int, lastInputOutput *TxnInputOutput, versionedData *MVHashMap) (valid bool) {
|
||||||
|
valid = true
|
||||||
|
|
||||||
|
for _, rd := range lastInputOutput.ReadSet(txIdx) {
|
||||||
|
mvResult := versionedData.Read(rd.Path, txIdx)
|
||||||
|
switch mvResult.Status() {
|
||||||
|
case MVReadResultDone:
|
||||||
|
valid = rd.Kind == ReadKindMap && rd.V == Version{
|
||||||
|
TxnIndex: mvResult.depIdx,
|
||||||
|
Incarnation: mvResult.incarnation,
|
||||||
|
}
|
||||||
|
case MVReadResultDependency:
|
||||||
|
valid = false
|
||||||
|
case MVReadResultNone:
|
||||||
|
valid = rd.Kind == ReadKindStorage // feels like an assertion?
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("should not happen - undefined mv read status: %ver", mvResult.Status()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !valid {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
344
core/blockstm/mvhashmap_test.go
Normal file
344
core/blockstm/mvhashmap_test.go
Normal file
|
|
@ -0,0 +1,344 @@
|
||||||
|
package blockstm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
var randomness = rand.Intn(10) + 10
|
||||||
|
|
||||||
|
// create test data for a given txIdx and incarnation
|
||||||
|
func valueFor(txIdx, inc int) []byte {
|
||||||
|
return []byte(fmt.Sprintf("%ver:%ver:%ver", txIdx*5, txIdx+inc, inc*5))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCommonAddress(i int) common.Address {
|
||||||
|
return common.BigToAddress(big.NewInt(int64(i % randomness)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHelperFunctions(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ap1 := NewAddressKey(getCommonAddress(1))
|
||||||
|
ap2 := NewAddressKey(getCommonAddress(2))
|
||||||
|
|
||||||
|
mvh := MakeMVHashMap()
|
||||||
|
|
||||||
|
mvh.Write(ap1, Version{0, 1}, valueFor(0, 1))
|
||||||
|
mvh.Write(ap1, Version{0, 2}, valueFor(0, 2))
|
||||||
|
res := mvh.Read(ap1, 0)
|
||||||
|
require.Equal(t, -1, res.DepIdx())
|
||||||
|
require.Equal(t, -1, res.Incarnation())
|
||||||
|
require.Equal(t, 2, res.Status())
|
||||||
|
|
||||||
|
mvh.Write(ap2, Version{1, 1}, valueFor(1, 1))
|
||||||
|
mvh.Write(ap2, Version{1, 2}, valueFor(1, 2))
|
||||||
|
res = mvh.Read(ap2, 1)
|
||||||
|
require.Equal(t, -1, res.DepIdx())
|
||||||
|
require.Equal(t, -1, res.Incarnation())
|
||||||
|
require.Equal(t, 2, res.Status())
|
||||||
|
|
||||||
|
mvh.Write(ap1, Version{2, 1}, valueFor(2, 1))
|
||||||
|
mvh.Write(ap1, Version{2, 2}, valueFor(2, 2))
|
||||||
|
res = mvh.Read(ap1, 2)
|
||||||
|
require.Equal(t, 0, res.DepIdx())
|
||||||
|
require.Equal(t, 2, res.Incarnation())
|
||||||
|
require.Equal(t, valueFor(0, 2), res.Value().([]byte))
|
||||||
|
require.Equal(t, 0, res.Status())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlushMVWrite(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ap1 := NewAddressKey(getCommonAddress(1))
|
||||||
|
ap2 := NewAddressKey(getCommonAddress(2))
|
||||||
|
|
||||||
|
mvh := MakeMVHashMap()
|
||||||
|
|
||||||
|
var res MVReadResult
|
||||||
|
|
||||||
|
wd := []WriteDescriptor{}
|
||||||
|
|
||||||
|
wd = append(wd, WriteDescriptor{
|
||||||
|
Path: ap1,
|
||||||
|
V: Version{0, 1},
|
||||||
|
Val: valueFor(0, 1),
|
||||||
|
})
|
||||||
|
wd = append(wd, WriteDescriptor{
|
||||||
|
Path: ap1,
|
||||||
|
V: Version{0, 2},
|
||||||
|
Val: valueFor(0, 2),
|
||||||
|
})
|
||||||
|
wd = append(wd, WriteDescriptor{
|
||||||
|
Path: ap2,
|
||||||
|
V: Version{1, 1},
|
||||||
|
Val: valueFor(1, 1),
|
||||||
|
})
|
||||||
|
wd = append(wd, WriteDescriptor{
|
||||||
|
Path: ap2,
|
||||||
|
V: Version{1, 2},
|
||||||
|
Val: valueFor(1, 2),
|
||||||
|
})
|
||||||
|
wd = append(wd, WriteDescriptor{
|
||||||
|
Path: ap1,
|
||||||
|
V: Version{2, 1},
|
||||||
|
Val: valueFor(2, 1),
|
||||||
|
})
|
||||||
|
wd = append(wd, WriteDescriptor{
|
||||||
|
Path: ap1,
|
||||||
|
V: Version{2, 2},
|
||||||
|
Val: valueFor(2, 2),
|
||||||
|
})
|
||||||
|
|
||||||
|
mvh.FlushMVWriteSet(wd)
|
||||||
|
|
||||||
|
res = mvh.Read(ap1, 0)
|
||||||
|
require.Equal(t, -1, res.DepIdx())
|
||||||
|
require.Equal(t, -1, res.Incarnation())
|
||||||
|
require.Equal(t, 2, res.Status())
|
||||||
|
|
||||||
|
res = mvh.Read(ap2, 1)
|
||||||
|
require.Equal(t, -1, res.DepIdx())
|
||||||
|
require.Equal(t, -1, res.Incarnation())
|
||||||
|
require.Equal(t, 2, res.Status())
|
||||||
|
|
||||||
|
res = mvh.Read(ap1, 2)
|
||||||
|
require.Equal(t, 0, res.DepIdx())
|
||||||
|
require.Equal(t, 2, res.Incarnation())
|
||||||
|
require.Equal(t, valueFor(0, 2), res.Value().([]byte))
|
||||||
|
require.Equal(t, 0, res.Status())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO - handle panic
|
||||||
|
func TestLowerIncarnation(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ap1 := NewAddressKey(getCommonAddress(1))
|
||||||
|
|
||||||
|
mvh := MakeMVHashMap()
|
||||||
|
|
||||||
|
mvh.Write(ap1, Version{0, 2}, valueFor(0, 2))
|
||||||
|
mvh.Read(ap1, 0)
|
||||||
|
mvh.Write(ap1, Version{1, 2}, valueFor(1, 2))
|
||||||
|
mvh.Write(ap1, Version{0, 5}, valueFor(0, 5))
|
||||||
|
mvh.Write(ap1, Version{1, 5}, valueFor(1, 5))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarkEstimate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ap1 := NewAddressKey(getCommonAddress(1))
|
||||||
|
|
||||||
|
mvh := MakeMVHashMap()
|
||||||
|
|
||||||
|
mvh.Write(ap1, Version{7, 2}, valueFor(7, 2))
|
||||||
|
mvh.MarkEstimate(ap1, 7)
|
||||||
|
mvh.Write(ap1, Version{7, 4}, valueFor(7, 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMVHashMapBasics(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// memory locations
|
||||||
|
ap1 := NewAddressKey(getCommonAddress(1))
|
||||||
|
ap2 := NewAddressKey(getCommonAddress(2))
|
||||||
|
ap3 := NewAddressKey(getCommonAddress(3))
|
||||||
|
|
||||||
|
mvh := MakeMVHashMap()
|
||||||
|
|
||||||
|
res := mvh.Read(ap1, 5)
|
||||||
|
require.Equal(t, -1, res.depIdx)
|
||||||
|
|
||||||
|
mvh.Write(ap1, Version{10, 1}, valueFor(10, 1))
|
||||||
|
|
||||||
|
res = mvh.Read(ap1, 9)
|
||||||
|
require.Equal(t, -1, res.depIdx, "reads that should go the the DB return dependency -1")
|
||||||
|
res = mvh.Read(ap1, 10)
|
||||||
|
require.Equal(t, -1, res.depIdx, "Read returns entries from smaller txns, not txn 10")
|
||||||
|
|
||||||
|
// Reads for a higher txn return the entry written by txn 10.
|
||||||
|
res = mvh.Read(ap1, 15)
|
||||||
|
require.Equal(t, 10, res.depIdx, "reads for a higher txn return the entry written by txn 10.")
|
||||||
|
require.Equal(t, 1, res.incarnation)
|
||||||
|
require.Equal(t, valueFor(10, 1), res.value)
|
||||||
|
|
||||||
|
// More writes.
|
||||||
|
mvh.Write(ap1, Version{12, 0}, valueFor(12, 0))
|
||||||
|
mvh.Write(ap1, Version{8, 3}, valueFor(8, 3))
|
||||||
|
|
||||||
|
// Verify reads.
|
||||||
|
res = mvh.Read(ap1, 15)
|
||||||
|
require.Equal(t, 12, res.depIdx)
|
||||||
|
require.Equal(t, 0, res.incarnation)
|
||||||
|
require.Equal(t, valueFor(12, 0), res.value)
|
||||||
|
|
||||||
|
res = mvh.Read(ap1, 11)
|
||||||
|
require.Equal(t, 10, res.depIdx)
|
||||||
|
require.Equal(t, 1, res.incarnation)
|
||||||
|
require.Equal(t, valueFor(10, 1), res.value)
|
||||||
|
|
||||||
|
res = mvh.Read(ap1, 10)
|
||||||
|
require.Equal(t, 8, res.depIdx)
|
||||||
|
require.Equal(t, 3, res.incarnation)
|
||||||
|
require.Equal(t, valueFor(8, 3), res.value)
|
||||||
|
|
||||||
|
// Mark the entry written by 10 as an estimate.
|
||||||
|
mvh.MarkEstimate(ap1, 10)
|
||||||
|
|
||||||
|
res = mvh.Read(ap1, 11)
|
||||||
|
require.Equal(t, 10, res.depIdx)
|
||||||
|
require.Equal(t, -1, res.incarnation, "dep at tx 10 is now an estimate")
|
||||||
|
|
||||||
|
// Delete the entry written by 10, write to a different ap.
|
||||||
|
mvh.Delete(ap1, 10)
|
||||||
|
mvh.Write(ap2, Version{10, 2}, valueFor(10, 2))
|
||||||
|
|
||||||
|
// Read by txn 11 no longer observes entry from txn 10.
|
||||||
|
res = mvh.Read(ap1, 11)
|
||||||
|
require.Equal(t, 8, res.depIdx)
|
||||||
|
require.Equal(t, 3, res.incarnation)
|
||||||
|
require.Equal(t, valueFor(8, 3), res.value)
|
||||||
|
|
||||||
|
// Reads, writes for ap2 and ap3.
|
||||||
|
mvh.Write(ap2, Version{5, 0}, valueFor(5, 0))
|
||||||
|
mvh.Write(ap3, Version{20, 4}, valueFor(20, 4))
|
||||||
|
|
||||||
|
res = mvh.Read(ap2, 10)
|
||||||
|
require.Equal(t, 5, res.depIdx)
|
||||||
|
require.Equal(t, 0, res.incarnation)
|
||||||
|
require.Equal(t, valueFor(5, 0), res.value)
|
||||||
|
|
||||||
|
res = mvh.Read(ap3, 21)
|
||||||
|
require.Equal(t, 20, res.depIdx)
|
||||||
|
require.Equal(t, 4, res.incarnation)
|
||||||
|
require.Equal(t, valueFor(20, 4), res.value)
|
||||||
|
|
||||||
|
// Clear ap1 and ap3.
|
||||||
|
mvh.Delete(ap1, 12)
|
||||||
|
mvh.Delete(ap1, 8)
|
||||||
|
mvh.Delete(ap3, 20)
|
||||||
|
|
||||||
|
// Reads from ap1 and ap3 go to db.
|
||||||
|
res = mvh.Read(ap1, 30)
|
||||||
|
require.Equal(t, -1, res.depIdx)
|
||||||
|
|
||||||
|
res = mvh.Read(ap3, 30)
|
||||||
|
require.Equal(t, -1, res.depIdx)
|
||||||
|
|
||||||
|
// No-op delete at ap2 - doesn't panic because ap2 does exist
|
||||||
|
mvh.Delete(ap2, 11)
|
||||||
|
|
||||||
|
// Read entry by txn 10 at ap2.
|
||||||
|
res = mvh.Read(ap2, 15)
|
||||||
|
require.Equal(t, 10, res.depIdx)
|
||||||
|
require.Equal(t, 2, res.incarnation)
|
||||||
|
require.Equal(t, valueFor(10, 2), res.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkWriteTimeSameLocationDifferentTxIdx(b *testing.B) {
|
||||||
|
mvh2 := MakeMVHashMap()
|
||||||
|
ap2 := NewAddressKey(getCommonAddress(2))
|
||||||
|
|
||||||
|
randInts := []int{}
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
randInts = append(randInts, rand.Intn(1000000000000000))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
mvh2.Write(ap2, Version{randInts[i], 1}, valueFor(randInts[i], 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkReadTimeSameLocationDifferentTxIdx(b *testing.B) {
|
||||||
|
mvh2 := MakeMVHashMap()
|
||||||
|
ap2 := NewAddressKey(getCommonAddress(2))
|
||||||
|
txIdxSlice := []int{}
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
txIdx := rand.Intn(1000000000000000)
|
||||||
|
txIdxSlice = append(txIdxSlice, txIdx)
|
||||||
|
mvh2.Write(ap2, Version{txIdx, 1}, valueFor(txIdx, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for _, value := range txIdxSlice {
|
||||||
|
mvh2.Read(ap2, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeComplexity(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// for 1000000 read and write with no dependency at different memory location
|
||||||
|
mvh1 := MakeMVHashMap()
|
||||||
|
|
||||||
|
for i := 0; i < 1000000; i++ {
|
||||||
|
ap1 := NewAddressKey(getCommonAddress(i))
|
||||||
|
mvh1.Write(ap1, Version{i, 1}, valueFor(i, 1))
|
||||||
|
mvh1.Read(ap1, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// for 1000000 read and write with dependency at same memory location
|
||||||
|
mvh2 := MakeMVHashMap()
|
||||||
|
ap2 := NewAddressKey(getCommonAddress(2))
|
||||||
|
|
||||||
|
for i := 0; i < 1000000; i++ {
|
||||||
|
mvh2.Write(ap2, Version{i, 1}, valueFor(i, 1))
|
||||||
|
mvh2.Read(ap2, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteTimeSameLocationDifferentTxnIdx(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
mvh1 := MakeMVHashMap()
|
||||||
|
ap1 := NewAddressKey(getCommonAddress(1))
|
||||||
|
|
||||||
|
for i := 0; i < 1000000; i++ {
|
||||||
|
mvh1.Write(ap1, Version{i, 1}, valueFor(i, 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteTimeSameLocationSameTxnIdx(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
mvh1 := MakeMVHashMap()
|
||||||
|
ap1 := NewAddressKey(getCommonAddress(1))
|
||||||
|
|
||||||
|
for i := 0; i < 1000000; i++ {
|
||||||
|
mvh1.Write(ap1, Version{1, i}, valueFor(i, 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteTimeDifferentLocation(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
mvh1 := MakeMVHashMap()
|
||||||
|
|
||||||
|
for i := 0; i < 1000000; i++ {
|
||||||
|
ap1 := NewAddressKey(getCommonAddress(i))
|
||||||
|
mvh1.Write(ap1, Version{i, 1}, valueFor(i, 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadTimeSameLocation(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
mvh1 := MakeMVHashMap()
|
||||||
|
ap1 := NewAddressKey(getCommonAddress(1))
|
||||||
|
|
||||||
|
mvh1.Write(ap1, Version{1, 1}, valueFor(1, 1))
|
||||||
|
|
||||||
|
for i := 0; i < 1000000; i++ {
|
||||||
|
mvh1.Read(ap1, 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
225
core/blockstm/status.go
Normal file
225
core/blockstm/status.go
Normal file
|
|
@ -0,0 +1,225 @@
|
||||||
|
package blockstm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeStatusManager(numTasks int) (t taskStatusManager) {
|
||||||
|
t.pending = make([]int, numTasks)
|
||||||
|
for i := 0; i < numTasks; i++ {
|
||||||
|
t.pending[i] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
t.dependency = make(map[int]map[int]bool, numTasks)
|
||||||
|
t.blocker = make(map[int]map[int]bool, numTasks)
|
||||||
|
|
||||||
|
for i := 0; i < numTasks; i++ {
|
||||||
|
t.blocker[i] = make(map[int]bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type taskStatusManager struct {
|
||||||
|
pending []int
|
||||||
|
inProgress []int
|
||||||
|
complete []int
|
||||||
|
dependency map[int]map[int]bool
|
||||||
|
blocker map[int]map[int]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func insertInList(l []int, v int) []int {
|
||||||
|
if len(l) == 0 || v > l[len(l)-1] {
|
||||||
|
return append(l, v)
|
||||||
|
} else {
|
||||||
|
x := sort.SearchInts(l, v)
|
||||||
|
if x < len(l) && l[x] == v {
|
||||||
|
// already in list
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
a := append(l[:x+1], l[x:]...)
|
||||||
|
a[x] = v
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *taskStatusManager) takeNextPending() int {
|
||||||
|
if len(m.pending) == 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
x := m.pending[0]
|
||||||
|
m.pending = m.pending[1:]
|
||||||
|
m.inProgress = insertInList(m.inProgress, x)
|
||||||
|
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasNoGap(l []int) bool {
|
||||||
|
return l[0]+len(l) == l[len(l)-1]+1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m taskStatusManager) maxAllComplete() int {
|
||||||
|
if len(m.complete) == 0 || m.complete[0] != 0 {
|
||||||
|
return -1
|
||||||
|
} else if m.complete[len(m.complete)-1] == len(m.complete)-1 {
|
||||||
|
return m.complete[len(m.complete)-1]
|
||||||
|
} else {
|
||||||
|
for i := len(m.complete) - 2; i >= 0; i-- {
|
||||||
|
if hasNoGap(m.complete[:i+1]) {
|
||||||
|
return m.complete[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *taskStatusManager) pushPending(tx int) {
|
||||||
|
m.pending = insertInList(m.pending, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeFromList(l []int, v int, expect bool) []int {
|
||||||
|
x := sort.SearchInts(l, v)
|
||||||
|
if x == -1 || l[x] != v {
|
||||||
|
if expect {
|
||||||
|
panic(fmt.Errorf("should not happen - element expected in list"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
switch x {
|
||||||
|
case 0:
|
||||||
|
return l[1:]
|
||||||
|
case len(l) - 1:
|
||||||
|
return l[:len(l)-1]
|
||||||
|
default:
|
||||||
|
return append(l[:x], l[x+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *taskStatusManager) markComplete(tx int) {
|
||||||
|
m.inProgress = removeFromList(m.inProgress, tx, true)
|
||||||
|
m.complete = insertInList(m.complete, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *taskStatusManager) minPending() int {
|
||||||
|
if len(m.pending) == 0 {
|
||||||
|
return -1
|
||||||
|
} else {
|
||||||
|
return m.pending[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *taskStatusManager) countComplete() int {
|
||||||
|
return len(m.complete)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *taskStatusManager) addDependencies(blocker int, dependent int) bool {
|
||||||
|
if blocker < 0 || blocker >= dependent {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
curblockers := m.blocker[dependent]
|
||||||
|
|
||||||
|
if m.checkComplete(blocker) {
|
||||||
|
// Blocker has already completed
|
||||||
|
delete(curblockers, blocker)
|
||||||
|
|
||||||
|
return len(curblockers) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := m.dependency[blocker]; !ok {
|
||||||
|
m.dependency[blocker] = make(map[int]bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.dependency[blocker][dependent] = true
|
||||||
|
curblockers[blocker] = true
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *taskStatusManager) isBlocked(tx int) bool {
|
||||||
|
return len(m.blocker[tx]) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *taskStatusManager) removeDependency(tx int) {
|
||||||
|
if deps, ok := m.dependency[tx]; ok && len(deps) > 0 {
|
||||||
|
for k := range deps {
|
||||||
|
delete(m.blocker[k], tx)
|
||||||
|
|
||||||
|
if len(m.blocker[k]) == 0 {
|
||||||
|
if !m.checkComplete(k) && !m.checkPending(k) && !m.checkInProgress(k) {
|
||||||
|
m.pushPending(k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(m.dependency, tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *taskStatusManager) clearInProgress(tx int) {
|
||||||
|
m.inProgress = removeFromList(m.inProgress, tx, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *taskStatusManager) checkInProgress(tx int) bool {
|
||||||
|
x := sort.SearchInts(m.inProgress, tx)
|
||||||
|
if x < len(m.inProgress) && m.inProgress[x] == tx {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *taskStatusManager) checkPending(tx int) bool {
|
||||||
|
x := sort.SearchInts(m.pending, tx)
|
||||||
|
if x < len(m.pending) && m.pending[x] == tx {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *taskStatusManager) checkComplete(tx int) bool {
|
||||||
|
x := sort.SearchInts(m.complete, tx)
|
||||||
|
if x < len(m.complete) && m.complete[x] == tx {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRevalidationRange: this range will be all tasks from tx (inclusive) that are not currently in progress up to the
|
||||||
|
//
|
||||||
|
// 'all complete' limit
|
||||||
|
func (m *taskStatusManager) getRevalidationRange(txFrom int) (ret []int) {
|
||||||
|
max := m.maxAllComplete() // haven't learned to trust compilers :)
|
||||||
|
for x := txFrom; x <= max; x++ {
|
||||||
|
if !m.checkInProgress(x) {
|
||||||
|
ret = append(ret, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *taskStatusManager) pushPendingSet(set []int) {
|
||||||
|
for _, v := range set {
|
||||||
|
if m.checkComplete(v) {
|
||||||
|
m.clearComplete(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.pushPending(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *taskStatusManager) clearComplete(tx int) {
|
||||||
|
m.complete = removeFromList(m.complete, tx, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *taskStatusManager) clearPending(tx int) {
|
||||||
|
m.pending = removeFromList(m.pending, tx, false)
|
||||||
|
}
|
||||||
82
core/blockstm/status_test.go
Normal file
82
core/blockstm/status_test.go
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
package blockstm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStatusBasics(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
s := makeStatusManager(10)
|
||||||
|
|
||||||
|
x := s.takeNextPending()
|
||||||
|
require.Equal(t, 0, x)
|
||||||
|
require.True(t, s.checkInProgress(x))
|
||||||
|
|
||||||
|
x = s.takeNextPending()
|
||||||
|
require.Equal(t, 1, x)
|
||||||
|
require.True(t, s.checkInProgress(x))
|
||||||
|
|
||||||
|
x = s.takeNextPending()
|
||||||
|
require.Equal(t, 2, x)
|
||||||
|
require.True(t, s.checkInProgress(x))
|
||||||
|
|
||||||
|
s.markComplete(0)
|
||||||
|
require.False(t, s.checkInProgress(0))
|
||||||
|
s.markComplete(1)
|
||||||
|
s.markComplete(2)
|
||||||
|
require.False(t, s.checkInProgress(1))
|
||||||
|
require.False(t, s.checkInProgress(2))
|
||||||
|
require.Equal(t, 2, s.maxAllComplete())
|
||||||
|
|
||||||
|
x = s.takeNextPending()
|
||||||
|
require.Equal(t, 3, x)
|
||||||
|
|
||||||
|
x = s.takeNextPending()
|
||||||
|
require.Equal(t, 4, x)
|
||||||
|
|
||||||
|
s.markComplete(x)
|
||||||
|
require.False(t, s.checkInProgress(4))
|
||||||
|
require.Equal(t, 2, s.maxAllComplete(), "zero should still be min complete")
|
||||||
|
|
||||||
|
exp := []int{1, 2}
|
||||||
|
require.Equal(t, exp, s.getRevalidationRange(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMaxComplete(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
s := makeStatusManager(10)
|
||||||
|
|
||||||
|
for {
|
||||||
|
tx := s.takeNextPending()
|
||||||
|
|
||||||
|
if tx == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if tx != 7 {
|
||||||
|
s.markComplete(tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Equal(t, 6, s.maxAllComplete())
|
||||||
|
|
||||||
|
s2 := makeStatusManager(10)
|
||||||
|
|
||||||
|
for {
|
||||||
|
tx := s2.takeNextPending()
|
||||||
|
|
||||||
|
if tx == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s2.markComplete(2)
|
||||||
|
s2.markComplete(4)
|
||||||
|
require.Equal(t, -1, s2.maxAllComplete())
|
||||||
|
|
||||||
|
s2.complete = insertInList(s2.complete, 4)
|
||||||
|
require.Equal(t, 2, s2.countComplete())
|
||||||
|
}
|
||||||
106
core/blockstm/txio.go
Normal file
106
core/blockstm/txio.go
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
package blockstm
|
||||||
|
|
||||||
|
const (
|
||||||
|
ReadKindMap = 0
|
||||||
|
ReadKindStorage = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
type ReadDescriptor struct {
|
||||||
|
Path Key
|
||||||
|
Kind int
|
||||||
|
V Version
|
||||||
|
}
|
||||||
|
|
||||||
|
type WriteDescriptor struct {
|
||||||
|
Path Key
|
||||||
|
V Version
|
||||||
|
Val interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TxnInput []ReadDescriptor
|
||||||
|
type TxnOutput []WriteDescriptor
|
||||||
|
|
||||||
|
// hasNewWrite: returns true if the current set has a new write compared to the input
|
||||||
|
func (txo TxnOutput) hasNewWrite(cmpSet []WriteDescriptor) bool {
|
||||||
|
if len(txo) == 0 {
|
||||||
|
return false
|
||||||
|
} else if len(cmpSet) == 0 || len(txo) > len(cmpSet) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
cmpMap := map[Key]bool{cmpSet[0].Path: true}
|
||||||
|
|
||||||
|
for i := 1; i < len(cmpSet); i++ {
|
||||||
|
cmpMap[cmpSet[i].Path] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range txo {
|
||||||
|
if !cmpMap[v.Path] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type TxnInputOutput struct {
|
||||||
|
inputs []TxnInput
|
||||||
|
outputs []TxnOutput // write sets that should be checked during validation
|
||||||
|
outputsSet []map[Key]struct{}
|
||||||
|
allOutputs []TxnOutput // entire write sets in MVHashMap. allOutputs should always be a parent set of outputs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (io *TxnInputOutput) ReadSet(txnIdx int) []ReadDescriptor {
|
||||||
|
return io.inputs[txnIdx]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (io *TxnInputOutput) WriteSet(txnIdx int) []WriteDescriptor {
|
||||||
|
return io.outputs[txnIdx]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (io *TxnInputOutput) AllWriteSet(txnIdx int) []WriteDescriptor {
|
||||||
|
return io.allOutputs[txnIdx]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (io *TxnInputOutput) HasWritten(txnIdx int, k Key) bool {
|
||||||
|
_, ok := io.outputsSet[txnIdx][k]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeTxnInputOutput(numTx int) *TxnInputOutput {
|
||||||
|
return &TxnInputOutput{
|
||||||
|
inputs: make([]TxnInput, numTx),
|
||||||
|
outputs: make([]TxnOutput, numTx),
|
||||||
|
outputsSet: make([]map[Key]struct{}, numTx),
|
||||||
|
allOutputs: make([]TxnOutput, numTx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (io *TxnInputOutput) recordRead(txId int, input []ReadDescriptor) {
|
||||||
|
io.inputs[txId] = input
|
||||||
|
}
|
||||||
|
|
||||||
|
func (io *TxnInputOutput) recordWrite(txId int, output []WriteDescriptor) {
|
||||||
|
io.outputs[txId] = output
|
||||||
|
io.outputsSet[txId] = make(map[Key]struct{}, len(output))
|
||||||
|
|
||||||
|
for _, v := range output {
|
||||||
|
io.outputsSet[txId][v.Path] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (io *TxnInputOutput) recordAllWrite(txId int, output []WriteDescriptor) {
|
||||||
|
io.allOutputs[txId] = output
|
||||||
|
}
|
||||||
|
|
||||||
|
func (io *TxnInputOutput) RecordReadAtOnce(inputs [][]ReadDescriptor) {
|
||||||
|
for ind, val := range inputs {
|
||||||
|
io.inputs[ind] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (io *TxnInputOutput) RecordAllWriteAtOnce(outputs [][]WriteDescriptor) {
|
||||||
|
for ind, val := range outputs {
|
||||||
|
io.allOutputs[ind] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -434,6 +434,19 @@ func makeBlockChainWithGenesis(genesis *Genesis, n int, engine consensus.Engine,
|
||||||
return db, blocks
|
return db, blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// makeBlockChain creates a deterministic chain of blocks rooted at parent with fake invalid transactions.
|
||||||
|
func makeFakeNonEmptyBlockChain(parent *types.Block, n int, engine consensus.Engine, db ethdb.Database, seed int, numTx int) []*types.Block {
|
||||||
|
blocks, _ := GenerateChain(params.TestChainConfig, parent, engine, db, n, func(i int, b *BlockGen) {
|
||||||
|
addr := common.Address{0: byte(seed), 19: byte(i)}
|
||||||
|
b.SetCoinbase(addr)
|
||||||
|
for j := 0; j < numTx; j++ {
|
||||||
|
b.txs = append(b.txs, types.NewTransaction(0, addr, big.NewInt(1000), params.TxGas, nil, nil))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return blocks
|
||||||
|
}
|
||||||
|
|
||||||
type fakeChainReader struct {
|
type fakeChainReader struct {
|
||||||
config *params.ChainConfig
|
config *params.ChainConfig
|
||||||
stateSyncData []*types.StateSyncData
|
stateSyncData []*types.StateSyncData
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
|
|
@ -83,12 +84,18 @@ func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash
|
||||||
// Then fill up with [refHash.p, refHash.pp, refHash.ppp, ...]
|
// Then fill up with [refHash.p, refHash.pp, refHash.ppp, ...]
|
||||||
var cache []common.Hash
|
var cache []common.Hash
|
||||||
|
|
||||||
|
cacheMutex := &sync.Mutex{}
|
||||||
|
|
||||||
return func(n uint64) common.Hash {
|
return func(n uint64) common.Hash {
|
||||||
if ref.Number.Uint64() <= n {
|
if ref.Number.Uint64() <= n {
|
||||||
// This situation can happen if we're doing tracing and using
|
// This situation can happen if we're doing tracing and using
|
||||||
// block overrides.
|
// block overrides.
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cacheMutex.Lock()
|
||||||
|
defer cacheMutex.Unlock()
|
||||||
|
|
||||||
// If there's no hash cache yet, make one
|
// If there's no hash cache yet, make one
|
||||||
if len(cache) == 0 {
|
if len(cache) == 0 {
|
||||||
cache = append(cache, ref.ParentHash)
|
cache = append(cache, ref.ParentHash)
|
||||||
|
|
|
||||||
429
core/parallel_state_processor.go
Normal file
429
core/parallel_state_processor.go
Normal file
|
|
@ -0,0 +1,429 @@
|
||||||
|
// Copyright 2015 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
|
"github.com/ethereum/go-ethereum/core/blockstm"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ParallelEVMConfig struct {
|
||||||
|
Enable bool
|
||||||
|
SpeculativeProcesses int
|
||||||
|
}
|
||||||
|
|
||||||
|
// StateProcessor is a basic Processor, which takes care of transitioning
|
||||||
|
// state from one point to another.
|
||||||
|
//
|
||||||
|
// StateProcessor implements Processor.
|
||||||
|
type ParallelStateProcessor struct {
|
||||||
|
config *params.ChainConfig // Chain configuration options
|
||||||
|
bc *BlockChain // Canonical block chain
|
||||||
|
engine consensus.Engine // Consensus engine used for block rewards
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStateProcessor initialises a new StateProcessor.
|
||||||
|
func NewParallelStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *ParallelStateProcessor {
|
||||||
|
return &ParallelStateProcessor{
|
||||||
|
config: config,
|
||||||
|
bc: bc,
|
||||||
|
engine: engine,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExecutionTask struct {
|
||||||
|
msg types.Message
|
||||||
|
config *params.ChainConfig
|
||||||
|
|
||||||
|
gasLimit uint64
|
||||||
|
blockNumber *big.Int
|
||||||
|
blockHash common.Hash
|
||||||
|
tx *types.Transaction
|
||||||
|
index int
|
||||||
|
statedb *state.StateDB // State database that stores the modified values after tx execution.
|
||||||
|
cleanStateDB *state.StateDB // A clean copy of the initial statedb. It should not be modified.
|
||||||
|
finalStateDB *state.StateDB // The final statedb.
|
||||||
|
header *types.Header
|
||||||
|
blockChain *BlockChain
|
||||||
|
evmConfig vm.Config
|
||||||
|
result *ExecutionResult
|
||||||
|
shouldDelayFeeCal *bool
|
||||||
|
shouldRerunWithoutFeeDelay bool
|
||||||
|
sender common.Address
|
||||||
|
totalUsedGas *uint64
|
||||||
|
receipts *types.Receipts
|
||||||
|
allLogs *[]*types.Log
|
||||||
|
|
||||||
|
// length of dependencies -> 2 + k (k = a whole number)
|
||||||
|
// first 2 element in dependencies -> transaction index, and flag representing if delay is allowed or not
|
||||||
|
// (0 -> delay is not allowed, 1 -> delay is allowed)
|
||||||
|
// next k elements in dependencies -> transaction indexes on which transaction i is dependent on
|
||||||
|
dependencies []int
|
||||||
|
coinbase common.Address
|
||||||
|
blockContext vm.BlockContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *ExecutionTask) Execute(mvh *blockstm.MVHashMap, incarnation int) (err error) {
|
||||||
|
task.statedb = task.cleanStateDB.Copy()
|
||||||
|
task.statedb.Prepare(task.tx.Hash(), task.index)
|
||||||
|
task.statedb.SetMVHashmap(mvh)
|
||||||
|
task.statedb.SetIncarnation(incarnation)
|
||||||
|
|
||||||
|
evm := vm.NewEVM(task.blockContext, vm.TxContext{}, task.statedb, task.config, task.evmConfig)
|
||||||
|
|
||||||
|
// Create a new context to be used in the EVM environment.
|
||||||
|
txContext := NewEVMTxContext(task.msg)
|
||||||
|
evm.Reset(txContext, task.statedb)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
// In some pre-matured executions, EVM will panic. Recover from panic and retry the execution.
|
||||||
|
log.Debug("Recovered from EVM failure.", "Error:", r)
|
||||||
|
|
||||||
|
err = blockstm.ErrExecAbortError{Dependency: task.statedb.DepTxIndex()}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Apply the transaction to the current state (included in the env).
|
||||||
|
if *task.shouldDelayFeeCal {
|
||||||
|
task.result, err = ApplyMessageNoFeeBurnOrTip(evm, task.msg, new(GasPool).AddGas(task.gasLimit), nil)
|
||||||
|
|
||||||
|
if task.result == nil || err != nil {
|
||||||
|
return blockstm.ErrExecAbortError{Dependency: task.statedb.DepTxIndex(), OriginError: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
reads := task.statedb.MVReadMap()
|
||||||
|
|
||||||
|
if _, ok := reads[blockstm.NewSubpathKey(task.blockContext.Coinbase, state.BalancePath)]; ok {
|
||||||
|
log.Info("Coinbase is in MVReadMap", "address", task.blockContext.Coinbase)
|
||||||
|
|
||||||
|
task.shouldRerunWithoutFeeDelay = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := reads[blockstm.NewSubpathKey(task.result.BurntContractAddress, state.BalancePath)]; ok {
|
||||||
|
log.Info("BurntContractAddress is in MVReadMap", "address", task.result.BurntContractAddress)
|
||||||
|
|
||||||
|
task.shouldRerunWithoutFeeDelay = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
task.result, err = ApplyMessage(evm, task.msg, new(GasPool).AddGas(task.gasLimit), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if task.statedb.HadInvalidRead() || err != nil {
|
||||||
|
err = blockstm.ErrExecAbortError{Dependency: task.statedb.DepTxIndex(), OriginError: err}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
task.statedb.Finalise(task.config.IsEIP158(task.blockNumber))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *ExecutionTask) MVReadList() []blockstm.ReadDescriptor {
|
||||||
|
return task.statedb.MVReadList()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *ExecutionTask) MVWriteList() []blockstm.WriteDescriptor {
|
||||||
|
return task.statedb.MVWriteList()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *ExecutionTask) MVFullWriteList() []blockstm.WriteDescriptor {
|
||||||
|
return task.statedb.MVFullWriteList()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *ExecutionTask) Sender() common.Address {
|
||||||
|
return task.sender
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *ExecutionTask) Hash() common.Hash {
|
||||||
|
return task.tx.Hash()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *ExecutionTask) Dependencies() []int {
|
||||||
|
return task.dependencies
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *ExecutionTask) Settle() {
|
||||||
|
task.finalStateDB.Prepare(task.tx.Hash(), task.index)
|
||||||
|
|
||||||
|
coinbaseBalance := task.finalStateDB.GetBalance(task.coinbase)
|
||||||
|
|
||||||
|
task.finalStateDB.ApplyMVWriteSet(task.statedb.MVFullWriteList())
|
||||||
|
|
||||||
|
for _, l := range task.statedb.GetLogs(task.tx.Hash(), task.blockHash) {
|
||||||
|
task.finalStateDB.AddLog(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *task.shouldDelayFeeCal {
|
||||||
|
if task.config.IsLondon(task.blockNumber) {
|
||||||
|
task.finalStateDB.AddBalance(task.result.BurntContractAddress, task.result.FeeBurnt)
|
||||||
|
}
|
||||||
|
|
||||||
|
task.finalStateDB.AddBalance(task.coinbase, task.result.FeeTipped)
|
||||||
|
output1 := new(big.Int).SetBytes(task.result.SenderInitBalance.Bytes())
|
||||||
|
output2 := new(big.Int).SetBytes(coinbaseBalance.Bytes())
|
||||||
|
|
||||||
|
// Deprecating transfer log and will be removed in future fork. PLEASE DO NOT USE this transfer log going forward. Parameters won't get updated as expected going forward with EIP1559
|
||||||
|
// add transfer log
|
||||||
|
AddFeeTransferLog(
|
||||||
|
task.finalStateDB,
|
||||||
|
|
||||||
|
task.msg.From(),
|
||||||
|
task.coinbase,
|
||||||
|
|
||||||
|
task.result.FeeTipped,
|
||||||
|
task.result.SenderInitBalance,
|
||||||
|
coinbaseBalance,
|
||||||
|
output1.Sub(output1, task.result.FeeTipped),
|
||||||
|
output2.Add(output2, task.result.FeeTipped),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range task.statedb.Preimages() {
|
||||||
|
task.finalStateDB.AddPreimage(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the state with pending changes.
|
||||||
|
var root []byte
|
||||||
|
|
||||||
|
if task.config.IsByzantium(task.blockNumber) {
|
||||||
|
task.finalStateDB.Finalise(true)
|
||||||
|
} else {
|
||||||
|
root = task.finalStateDB.IntermediateRoot(task.config.IsEIP158(task.blockNumber)).Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
*task.totalUsedGas += task.result.UsedGas
|
||||||
|
|
||||||
|
// Create a new receipt for the transaction, storing the intermediate root and gas used
|
||||||
|
// by the tx.
|
||||||
|
receipt := &types.Receipt{Type: task.tx.Type(), PostState: root, CumulativeGasUsed: *task.totalUsedGas}
|
||||||
|
if task.result.Failed() {
|
||||||
|
receipt.Status = types.ReceiptStatusFailed
|
||||||
|
} else {
|
||||||
|
receipt.Status = types.ReceiptStatusSuccessful
|
||||||
|
}
|
||||||
|
|
||||||
|
receipt.TxHash = task.tx.Hash()
|
||||||
|
receipt.GasUsed = task.result.UsedGas
|
||||||
|
|
||||||
|
// If the transaction created a contract, store the creation address in the receipt.
|
||||||
|
if task.msg.To() == nil {
|
||||||
|
receipt.ContractAddress = crypto.CreateAddress(task.msg.From(), task.tx.Nonce())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the receipt logs and create the bloom filter.
|
||||||
|
receipt.Logs = task.finalStateDB.GetLogs(task.tx.Hash(), task.blockHash)
|
||||||
|
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||||
|
receipt.BlockHash = task.blockHash
|
||||||
|
receipt.BlockNumber = task.blockNumber
|
||||||
|
receipt.TransactionIndex = uint(task.finalStateDB.TxIndex())
|
||||||
|
|
||||||
|
*task.receipts = append(*task.receipts, receipt)
|
||||||
|
*task.allLogs = append(*task.allLogs, receipt.Logs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var parallelizabilityTimer = metrics.NewRegisteredTimer("block/parallelizability", nil)
|
||||||
|
|
||||||
|
// Process processes the state changes according to the Ethereum rules by running
|
||||||
|
// the transaction messages using the statedb and applying any rewards to both
|
||||||
|
// the processor (coinbase) and any included uncles.
|
||||||
|
//
|
||||||
|
// Process returns the receipts and logs accumulated during the process and
|
||||||
|
// returns the amount of gas that was used in the process. If any of the
|
||||||
|
// transactions failed to execute due to insufficient gas it will return an error.
|
||||||
|
// nolint:gocognit
|
||||||
|
func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config, interruptCtx context.Context) (types.Receipts, []*types.Log, uint64, error) {
|
||||||
|
blockstm.SetProcs(cfg.ParallelSpeculativeProcesses)
|
||||||
|
|
||||||
|
var (
|
||||||
|
receipts types.Receipts
|
||||||
|
header = block.Header()
|
||||||
|
blockHash = block.Hash()
|
||||||
|
blockNumber = block.Number()
|
||||||
|
allLogs []*types.Log
|
||||||
|
usedGas = new(uint64)
|
||||||
|
metadata bool
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mutate the block and state according to any hard-fork specs
|
||||||
|
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
|
||||||
|
misc.ApplyDAOHardFork(statedb)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks := make([]blockstm.ExecTask, 0, len(block.Transactions()))
|
||||||
|
|
||||||
|
shouldDelayFeeCal := true
|
||||||
|
|
||||||
|
coinbase, _ := p.bc.Engine().Author(header)
|
||||||
|
|
||||||
|
deps := GetDeps(block.Header().TxDependency)
|
||||||
|
|
||||||
|
if block.Header().TxDependency != nil {
|
||||||
|
metadata = true
|
||||||
|
}
|
||||||
|
|
||||||
|
blockContext := NewEVMBlockContext(header, p.bc, nil)
|
||||||
|
|
||||||
|
// Iterate over and process the individual transactions
|
||||||
|
for i, tx := range block.Transactions() {
|
||||||
|
msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), header.BaseFee)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("error creating message", "err", err)
|
||||||
|
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cleansdb := statedb.Copy()
|
||||||
|
|
||||||
|
if msg.From() == coinbase {
|
||||||
|
shouldDelayFeeCal = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(header.TxDependency) != len(block.Transactions()) {
|
||||||
|
task := &ExecutionTask{
|
||||||
|
msg: msg,
|
||||||
|
config: p.config,
|
||||||
|
gasLimit: block.GasLimit(),
|
||||||
|
blockNumber: blockNumber,
|
||||||
|
blockHash: blockHash,
|
||||||
|
tx: tx,
|
||||||
|
index: i,
|
||||||
|
cleanStateDB: cleansdb,
|
||||||
|
finalStateDB: statedb,
|
||||||
|
blockChain: p.bc,
|
||||||
|
header: header,
|
||||||
|
evmConfig: cfg,
|
||||||
|
shouldDelayFeeCal: &shouldDelayFeeCal,
|
||||||
|
sender: msg.From(),
|
||||||
|
totalUsedGas: usedGas,
|
||||||
|
receipts: &receipts,
|
||||||
|
allLogs: &allLogs,
|
||||||
|
dependencies: deps[i],
|
||||||
|
coinbase: coinbase,
|
||||||
|
blockContext: blockContext,
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks = append(tasks, task)
|
||||||
|
} else {
|
||||||
|
task := &ExecutionTask{
|
||||||
|
msg: msg,
|
||||||
|
config: p.config,
|
||||||
|
gasLimit: block.GasLimit(),
|
||||||
|
blockNumber: blockNumber,
|
||||||
|
blockHash: blockHash,
|
||||||
|
tx: tx,
|
||||||
|
index: i,
|
||||||
|
cleanStateDB: cleansdb,
|
||||||
|
finalStateDB: statedb,
|
||||||
|
blockChain: p.bc,
|
||||||
|
header: header,
|
||||||
|
evmConfig: cfg,
|
||||||
|
shouldDelayFeeCal: &shouldDelayFeeCal,
|
||||||
|
sender: msg.From(),
|
||||||
|
totalUsedGas: usedGas,
|
||||||
|
receipts: &receipts,
|
||||||
|
allLogs: &allLogs,
|
||||||
|
dependencies: nil,
|
||||||
|
coinbase: coinbase,
|
||||||
|
blockContext: blockContext,
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks = append(tasks, task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backupStateDB := statedb.Copy()
|
||||||
|
|
||||||
|
profile := false
|
||||||
|
result, err := blockstm.ExecuteParallel(tasks, profile, metadata, interruptCtx)
|
||||||
|
|
||||||
|
if err == nil && profile && result.Deps != nil {
|
||||||
|
_, weight := result.Deps.LongestPath(*result.Stats)
|
||||||
|
|
||||||
|
serialWeight := uint64(0)
|
||||||
|
|
||||||
|
for i := 0; i < len(result.Deps.GetVertices()); i++ {
|
||||||
|
serialWeight += (*result.Stats)[i].End - (*result.Stats)[i].Start
|
||||||
|
}
|
||||||
|
|
||||||
|
parallelizabilityTimer.Update(time.Duration(serialWeight * 100 / weight))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, task := range tasks {
|
||||||
|
task := task.(*ExecutionTask)
|
||||||
|
if task.shouldRerunWithoutFeeDelay {
|
||||||
|
shouldDelayFeeCal = false
|
||||||
|
|
||||||
|
statedb.StopPrefetcher()
|
||||||
|
*statedb = *backupStateDB
|
||||||
|
|
||||||
|
allLogs = []*types.Log{}
|
||||||
|
receipts = types.Receipts{}
|
||||||
|
usedGas = new(uint64)
|
||||||
|
|
||||||
|
for _, t := range tasks {
|
||||||
|
t := t.(*ExecutionTask)
|
||||||
|
t.finalStateDB = backupStateDB
|
||||||
|
t.allLogs = &allLogs
|
||||||
|
t.receipts = &receipts
|
||||||
|
t.totalUsedGas = usedGas
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = blockstm.ExecuteParallel(tasks, false, metadata, interruptCtx)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||||
|
p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles())
|
||||||
|
|
||||||
|
return receipts, allLogs, *usedGas, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDeps(txDependency [][]uint64) map[int][]int {
|
||||||
|
deps := make(map[int][]int)
|
||||||
|
|
||||||
|
for i := 0; i <= len(txDependency)-1; i++ {
|
||||||
|
deps[i] = []int{}
|
||||||
|
|
||||||
|
for j := 0; j <= len(txDependency[i])-1; j++ {
|
||||||
|
deps[i] = append(deps[i], int(txDependency[i][j]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return deps
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/blockstm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// journalEntry is a modification entry in the state change journal that can be
|
// journalEntry is a modification entry in the state change journal that can be
|
||||||
|
|
@ -148,6 +149,7 @@ type (
|
||||||
func (ch createObjectChange) revert(s *StateDB) {
|
func (ch createObjectChange) revert(s *StateDB) {
|
||||||
delete(s.stateObjects, *ch.account)
|
delete(s.stateObjects, *ch.account)
|
||||||
delete(s.stateObjectsDirty, *ch.account)
|
delete(s.stateObjectsDirty, *ch.account)
|
||||||
|
RevertWrite(s, blockstm.NewAddressKey(*ch.account))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch createObjectChange) dirtied() *common.Address {
|
func (ch createObjectChange) dirtied() *common.Address {
|
||||||
|
|
@ -156,6 +158,7 @@ func (ch createObjectChange) dirtied() *common.Address {
|
||||||
|
|
||||||
func (ch resetObjectChange) revert(s *StateDB) {
|
func (ch resetObjectChange) revert(s *StateDB) {
|
||||||
s.setStateObject(ch.prev)
|
s.setStateObject(ch.prev)
|
||||||
|
RevertWrite(s, blockstm.NewAddressKey(ch.prev.address))
|
||||||
if !ch.prevdestruct {
|
if !ch.prevdestruct {
|
||||||
delete(s.stateObjectsDestruct, ch.prev.address)
|
delete(s.stateObjectsDestruct, ch.prev.address)
|
||||||
}
|
}
|
||||||
|
|
@ -170,6 +173,7 @@ func (ch suicideChange) revert(s *StateDB) {
|
||||||
if obj != nil {
|
if obj != nil {
|
||||||
obj.suicided = ch.prev
|
obj.suicided = ch.prev
|
||||||
obj.setBalance(ch.prevbalance)
|
obj.setBalance(ch.prevbalance)
|
||||||
|
RevertWrite(s, blockstm.NewSubpathKey(*ch.account, SuicidePath))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,6 +208,7 @@ func (ch nonceChange) dirtied() *common.Address {
|
||||||
|
|
||||||
func (ch codeChange) revert(s *StateDB) {
|
func (ch codeChange) revert(s *StateDB) {
|
||||||
s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
|
s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
|
||||||
|
RevertWrite(s, blockstm.NewSubpathKey(*ch.account, CodePath))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch codeChange) dirtied() *common.Address {
|
func (ch codeChange) dirtied() *common.Address {
|
||||||
|
|
@ -212,6 +217,7 @@ func (ch codeChange) dirtied() *common.Address {
|
||||||
|
|
||||||
func (ch storageChange) revert(s *StateDB) {
|
func (ch storageChange) revert(s *StateDB) {
|
||||||
s.getStateObject(*ch.account).setState(ch.key, ch.prevalue)
|
s.getStateObject(*ch.account).setState(ch.key, ch.prevalue)
|
||||||
|
RevertWrite(s, blockstm.NewStateKey(*ch.account, ch.key))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch storageChange) dirtied() *common.Address {
|
func (ch storageChange) dirtied() *common.Address {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/blockstm"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
@ -78,6 +79,14 @@ type StateDB struct {
|
||||||
stateObjectsDirty map[common.Address]struct{} // State objects modified in the current execution
|
stateObjectsDirty map[common.Address]struct{} // State objects modified in the current execution
|
||||||
stateObjectsDestruct map[common.Address]struct{} // State objects destructed in the block
|
stateObjectsDestruct map[common.Address]struct{} // State objects destructed in the block
|
||||||
|
|
||||||
|
// Block-stm related fields
|
||||||
|
mvHashmap *blockstm.MVHashMap
|
||||||
|
incarnation int
|
||||||
|
readMap map[blockstm.Key]blockstm.ReadDescriptor
|
||||||
|
writeMap map[blockstm.Key]blockstm.WriteDescriptor
|
||||||
|
revertedKeys map[blockstm.Key]struct{}
|
||||||
|
dep int
|
||||||
|
|
||||||
// DB error.
|
// DB error.
|
||||||
// State objects are used by the consensus core and VM which are
|
// State objects are used by the consensus core and VM which are
|
||||||
// unable to deal with database-level errors. Any error that occurs
|
// unable to deal with database-level errors. Any error that occurs
|
||||||
|
|
@ -144,6 +153,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
|
||||||
stateObjectsPending: make(map[common.Address]struct{}),
|
stateObjectsPending: make(map[common.Address]struct{}),
|
||||||
stateObjectsDirty: make(map[common.Address]struct{}),
|
stateObjectsDirty: make(map[common.Address]struct{}),
|
||||||
stateObjectsDestruct: make(map[common.Address]struct{}),
|
stateObjectsDestruct: make(map[common.Address]struct{}),
|
||||||
|
revertedKeys: make(map[blockstm.Key]struct{}),
|
||||||
logs: make(map[common.Hash][]*types.Log),
|
logs: make(map[common.Hash][]*types.Log),
|
||||||
preimages: make(map[common.Hash][]byte),
|
preimages: make(map[common.Hash][]byte),
|
||||||
journal: newJournal(),
|
journal: newJournal(),
|
||||||
|
|
@ -160,6 +170,281 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
|
||||||
return sdb, nil
|
return sdb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewWithMVHashmap(root common.Hash, db Database, snaps *snapshot.Tree, mvhm *blockstm.MVHashMap) (*StateDB, error) {
|
||||||
|
if sdb, err := New(root, db, snaps); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
sdb.mvHashmap = mvhm
|
||||||
|
sdb.dep = -1
|
||||||
|
return sdb, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) SetMVHashmap(mvhm *blockstm.MVHashMap) {
|
||||||
|
s.mvHashmap = mvhm
|
||||||
|
s.dep = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) GetMVHashmap() *blockstm.MVHashMap {
|
||||||
|
return s.mvHashmap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) MVWriteList() []blockstm.WriteDescriptor {
|
||||||
|
writes := make([]blockstm.WriteDescriptor, 0, len(s.writeMap))
|
||||||
|
|
||||||
|
for _, v := range s.writeMap {
|
||||||
|
if _, ok := s.revertedKeys[v.Path]; !ok {
|
||||||
|
writes = append(writes, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return writes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) MVFullWriteList() []blockstm.WriteDescriptor {
|
||||||
|
writes := make([]blockstm.WriteDescriptor, 0, len(s.writeMap))
|
||||||
|
|
||||||
|
for _, v := range s.writeMap {
|
||||||
|
writes = append(writes, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return writes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) MVReadMap() map[blockstm.Key]blockstm.ReadDescriptor {
|
||||||
|
return s.readMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) MVReadList() []blockstm.ReadDescriptor {
|
||||||
|
reads := make([]blockstm.ReadDescriptor, 0, len(s.readMap))
|
||||||
|
|
||||||
|
for _, v := range s.MVReadMap() {
|
||||||
|
reads = append(reads, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return reads
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) ensureReadMap() {
|
||||||
|
if s.readMap == nil {
|
||||||
|
s.readMap = make(map[blockstm.Key]blockstm.ReadDescriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) ensureWriteMap() {
|
||||||
|
if s.writeMap == nil {
|
||||||
|
s.writeMap = make(map[blockstm.Key]blockstm.WriteDescriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) ClearReadMap() {
|
||||||
|
s.readMap = make(map[blockstm.Key]blockstm.ReadDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) ClearWriteMap() {
|
||||||
|
s.writeMap = make(map[blockstm.Key]blockstm.WriteDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) HadInvalidRead() bool {
|
||||||
|
return s.dep >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) DepTxIndex() int {
|
||||||
|
return s.dep
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) SetIncarnation(inc int) {
|
||||||
|
s.incarnation = inc
|
||||||
|
}
|
||||||
|
|
||||||
|
type StorageVal[T any] struct {
|
||||||
|
Value *T
|
||||||
|
}
|
||||||
|
|
||||||
|
func MVRead[T any](s *StateDB, k blockstm.Key, defaultV T, readStorage func(s *StateDB) T) (v T) {
|
||||||
|
if s.mvHashmap == nil {
|
||||||
|
return readStorage(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.ensureReadMap()
|
||||||
|
|
||||||
|
if s.writeMap != nil {
|
||||||
|
if _, ok := s.writeMap[k]; ok {
|
||||||
|
return readStorage(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !k.IsAddress() {
|
||||||
|
// If we are reading subpath from a deleted account, return default value instead of reading from MVHashmap
|
||||||
|
addr := k.GetAddress()
|
||||||
|
if s.getStateObject(addr) == nil {
|
||||||
|
return defaultV
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res := s.mvHashmap.Read(k, s.txIndex)
|
||||||
|
|
||||||
|
var rd blockstm.ReadDescriptor
|
||||||
|
|
||||||
|
rd.V = blockstm.Version{
|
||||||
|
TxnIndex: res.DepIdx(),
|
||||||
|
Incarnation: res.Incarnation(),
|
||||||
|
}
|
||||||
|
|
||||||
|
rd.Path = k
|
||||||
|
|
||||||
|
switch res.Status() {
|
||||||
|
case blockstm.MVReadResultDone:
|
||||||
|
{
|
||||||
|
v = readStorage(res.Value().(*StateDB))
|
||||||
|
rd.Kind = blockstm.ReadKindMap
|
||||||
|
}
|
||||||
|
case blockstm.MVReadResultDependency:
|
||||||
|
{
|
||||||
|
s.dep = res.DepIdx()
|
||||||
|
|
||||||
|
panic("Found dependency")
|
||||||
|
}
|
||||||
|
case blockstm.MVReadResultNone:
|
||||||
|
{
|
||||||
|
v = readStorage(s)
|
||||||
|
rd.Kind = blockstm.ReadKindStorage
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return defaultV
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: I assume we don't want to overwrite an existing read because this could - for example - change a storage
|
||||||
|
// read to map if the same value is read multiple times.
|
||||||
|
if _, ok := s.readMap[k]; !ok {
|
||||||
|
s.readMap[k] = rd
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func MVWrite(s *StateDB, k blockstm.Key) {
|
||||||
|
if s.mvHashmap != nil {
|
||||||
|
s.ensureWriteMap()
|
||||||
|
s.writeMap[k] = blockstm.WriteDescriptor{
|
||||||
|
Path: k,
|
||||||
|
V: s.Version(),
|
||||||
|
Val: s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RevertWrite(s *StateDB, k blockstm.Key) {
|
||||||
|
s.revertedKeys[k] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MVWritten(s *StateDB, k blockstm.Key) bool {
|
||||||
|
if s.mvHashmap == nil || s.writeMap == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := s.writeMap[k]
|
||||||
|
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlushMVWriteSet applies entries in the write set to MVHashMap. Note that this function does not clear the write set.
|
||||||
|
func (s *StateDB) FlushMVWriteSet() {
|
||||||
|
if s.mvHashmap != nil && s.writeMap != nil {
|
||||||
|
s.mvHashmap.FlushMVWriteSet(s.MVFullWriteList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyMVWriteSet applies entries in a given write set to StateDB. Note that this function does not change MVHashMap nor write set
|
||||||
|
// of the current StateDB.
|
||||||
|
func (s *StateDB) ApplyMVWriteSet(writes []blockstm.WriteDescriptor) {
|
||||||
|
for i := range writes {
|
||||||
|
path := writes[i].Path
|
||||||
|
sr := writes[i].Val.(*StateDB)
|
||||||
|
|
||||||
|
if path.IsState() {
|
||||||
|
addr := path.GetAddress()
|
||||||
|
stateKey := path.GetStateKey()
|
||||||
|
state := sr.GetState(addr, stateKey)
|
||||||
|
s.SetState(addr, stateKey, state)
|
||||||
|
} else if path.IsAddress() {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
addr := path.GetAddress()
|
||||||
|
switch path.GetSubpath() {
|
||||||
|
case BalancePath:
|
||||||
|
s.SetBalance(addr, sr.GetBalance(addr))
|
||||||
|
case NoncePath:
|
||||||
|
s.SetNonce(addr, sr.GetNonce(addr))
|
||||||
|
case CodePath:
|
||||||
|
s.SetCode(addr, sr.GetCode(addr))
|
||||||
|
case SuicidePath:
|
||||||
|
stateObject := sr.getDeletedStateObject(addr)
|
||||||
|
if stateObject != nil && stateObject.deleted {
|
||||||
|
s.Suicide(addr)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unknown key type: %d", path.GetSubpath()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DumpStruct struct {
|
||||||
|
TxIdx int
|
||||||
|
TxInc int
|
||||||
|
VerIdx int
|
||||||
|
VerInc int
|
||||||
|
Path []byte
|
||||||
|
Op string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReadMapDump gets readMap Dump of format: "TxIdx, Inc, Path, Read"
|
||||||
|
func (s *StateDB) GetReadMapDump() []DumpStruct {
|
||||||
|
readList := s.MVReadList()
|
||||||
|
res := make([]DumpStruct, 0, len(readList))
|
||||||
|
|
||||||
|
for _, val := range readList {
|
||||||
|
temp := &DumpStruct{
|
||||||
|
TxIdx: s.txIndex,
|
||||||
|
TxInc: s.incarnation,
|
||||||
|
VerIdx: val.V.TxnIndex,
|
||||||
|
VerInc: val.V.Incarnation,
|
||||||
|
Path: val.Path[:],
|
||||||
|
Op: "Read\n",
|
||||||
|
}
|
||||||
|
res = append(res, *temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWriteMapDump gets writeMap Dump of format: "TxIdx, Inc, Path, Write"
|
||||||
|
func (s *StateDB) GetWriteMapDump() []DumpStruct {
|
||||||
|
writeList := s.MVReadList()
|
||||||
|
res := make([]DumpStruct, 0, len(writeList))
|
||||||
|
|
||||||
|
for _, val := range writeList {
|
||||||
|
temp := &DumpStruct{
|
||||||
|
TxIdx: s.txIndex,
|
||||||
|
TxInc: s.incarnation,
|
||||||
|
VerIdx: val.V.TxnIndex,
|
||||||
|
VerInc: val.V.Incarnation,
|
||||||
|
Path: val.Path[:],
|
||||||
|
Op: "Write\n",
|
||||||
|
}
|
||||||
|
res = append(res, *temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEmptyMVHashMap adds empty MVHashMap to StateDB
|
||||||
|
func (s *StateDB) AddEmptyMVHashMap() {
|
||||||
|
mvh := blockstm.MakeMVHashMap()
|
||||||
|
s.mvHashmap = mvh
|
||||||
|
}
|
||||||
|
|
||||||
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
|
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
|
||||||
// state trie concurrently while the state is mutated so that when we reach the
|
// state trie concurrently while the state is mutated so that when we reach the
|
||||||
// commit phase, most of the needed data is already hot.
|
// commit phase, most of the needed data is already hot.
|
||||||
|
|
@ -267,22 +552,40 @@ func (s *StateDB) Empty(addr common.Address) bool {
|
||||||
return so == nil || so.empty()
|
return so == nil || so.empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a unique path for special fields (e.g. balance, code) in a state object.
|
||||||
|
// func subPath(prefix []byte, s uint8) [blockstm.KeyLength]byte {
|
||||||
|
// path := append(prefix, common.Hash{}.Bytes()...) // append a full empty hash to avoid collision with storage state
|
||||||
|
// path = append(path, s) // append the special field identifier
|
||||||
|
|
||||||
|
// return path
|
||||||
|
// }
|
||||||
|
|
||||||
|
const BalancePath = 1
|
||||||
|
const NoncePath = 2
|
||||||
|
const CodePath = 3
|
||||||
|
const SuicidePath = 4
|
||||||
|
|
||||||
// GetBalance retrieves the balance from the given address or 0 if object not found
|
// GetBalance retrieves the balance from the given address or 0 if object not found
|
||||||
func (s *StateDB) GetBalance(addr common.Address) *big.Int {
|
func (s *StateDB) GetBalance(addr common.Address) *big.Int {
|
||||||
stateObject := s.getStateObject(addr)
|
return MVRead(s, blockstm.NewSubpathKey(addr, BalancePath), common.Big0, func(s *StateDB) *big.Int {
|
||||||
if stateObject != nil {
|
stateObject := s.getStateObject(addr)
|
||||||
return stateObject.Balance()
|
if stateObject != nil {
|
||||||
}
|
return stateObject.Balance()
|
||||||
return common.Big0
|
}
|
||||||
|
|
||||||
|
return common.Big0
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) GetNonce(addr common.Address) uint64 {
|
func (s *StateDB) GetNonce(addr common.Address) uint64 {
|
||||||
stateObject := s.getStateObject(addr)
|
return MVRead(s, blockstm.NewSubpathKey(addr, NoncePath), 0, func(s *StateDB) uint64 {
|
||||||
if stateObject != nil {
|
stateObject := s.getStateObject(addr)
|
||||||
return stateObject.Nonce()
|
if stateObject != nil {
|
||||||
}
|
return stateObject.Nonce()
|
||||||
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxIndex returns the current transaction index set by Prepare.
|
// TxIndex returns the current transaction index set by Prepare.
|
||||||
|
|
@ -290,37 +593,52 @@ func (s *StateDB) TxIndex() int {
|
||||||
return s.txIndex
|
return s.txIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) GetCode(addr common.Address) []byte {
|
func (s *StateDB) Version() blockstm.Version {
|
||||||
stateObject := s.getStateObject(addr)
|
return blockstm.Version{
|
||||||
if stateObject != nil {
|
TxnIndex: s.txIndex,
|
||||||
return stateObject.Code(s.db)
|
Incarnation: s.incarnation,
|
||||||
}
|
}
|
||||||
return nil
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) GetCode(addr common.Address) []byte {
|
||||||
|
return MVRead(s, blockstm.NewSubpathKey(addr, CodePath), nil, func(s *StateDB) []byte {
|
||||||
|
stateObject := s.getStateObject(addr)
|
||||||
|
if stateObject != nil {
|
||||||
|
return stateObject.Code(s.db)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) GetCodeSize(addr common.Address) int {
|
func (s *StateDB) GetCodeSize(addr common.Address) int {
|
||||||
stateObject := s.getStateObject(addr)
|
return MVRead(s, blockstm.NewSubpathKey(addr, CodePath), 0, func(s *StateDB) int {
|
||||||
if stateObject != nil {
|
stateObject := s.getStateObject(addr)
|
||||||
return stateObject.CodeSize(s.db)
|
if stateObject != nil {
|
||||||
}
|
return stateObject.CodeSize(s.db)
|
||||||
return 0
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
|
func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
|
||||||
stateObject := s.getStateObject(addr)
|
return MVRead(s, blockstm.NewSubpathKey(addr, CodePath), common.Hash{}, func(s *StateDB) common.Hash {
|
||||||
if stateObject == nil {
|
stateObject := s.getStateObject(addr)
|
||||||
return common.Hash{}
|
if stateObject == nil {
|
||||||
}
|
return common.Hash{}
|
||||||
return common.BytesToHash(stateObject.CodeHash())
|
}
|
||||||
|
return common.BytesToHash(stateObject.CodeHash())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetState retrieves a value from the given account's storage trie.
|
// GetState retrieves a value from the given account's storage trie.
|
||||||
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
|
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
|
||||||
stateObject := s.getStateObject(addr)
|
return MVRead(s, blockstm.NewStateKey(addr, hash), common.Hash{}, func(s *StateDB) common.Hash {
|
||||||
if stateObject != nil {
|
stateObject := s.getStateObject(addr)
|
||||||
return stateObject.GetState(s.db, hash)
|
if stateObject != nil {
|
||||||
}
|
return stateObject.GetState(s.db, hash)
|
||||||
return common.Hash{}
|
}
|
||||||
|
return common.Hash{}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetProof returns the Merkle proof for a given account.
|
// GetProof returns the Merkle proof for a given account.
|
||||||
|
|
@ -354,11 +672,13 @@ func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte,
|
||||||
|
|
||||||
// GetCommittedState retrieves a value from the given account's committed storage trie.
|
// GetCommittedState retrieves a value from the given account's committed storage trie.
|
||||||
func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
|
func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
|
||||||
stateObject := s.getStateObject(addr)
|
return MVRead(s, blockstm.NewStateKey(addr, hash), common.Hash{}, func(s *StateDB) common.Hash {
|
||||||
if stateObject != nil {
|
stateObject := s.getStateObject(addr)
|
||||||
return stateObject.GetCommittedState(s.db, hash)
|
if stateObject != nil {
|
||||||
}
|
return stateObject.GetCommittedState(s.db, hash)
|
||||||
return common.Hash{}
|
}
|
||||||
|
return common.Hash{}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Database retrieves the low level database supporting the lower level trie ops.
|
// Database retrieves the low level database supporting the lower level trie ops.
|
||||||
|
|
@ -382,11 +702,13 @@ func (s *StateDB) StorageTrie(addr common.Address) (Trie, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) HasSuicided(addr common.Address) bool {
|
func (s *StateDB) HasSuicided(addr common.Address) bool {
|
||||||
stateObject := s.getStateObject(addr)
|
return MVRead(s, blockstm.NewSubpathKey(addr, SuicidePath), false, func(s *StateDB) bool {
|
||||||
if stateObject != nil {
|
stateObject := s.getStateObject(addr)
|
||||||
return stateObject.suicided
|
if stateObject != nil {
|
||||||
}
|
return stateObject.suicided
|
||||||
return false
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -396,44 +718,68 @@ func (s *StateDB) HasSuicided(addr common.Address) bool {
|
||||||
// AddBalance adds amount to the account associated with addr.
|
// AddBalance adds amount to the account associated with addr.
|
||||||
func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) {
|
func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) {
|
||||||
stateObject := s.GetOrNewStateObject(addr)
|
stateObject := s.GetOrNewStateObject(addr)
|
||||||
|
|
||||||
|
if s.mvHashmap != nil {
|
||||||
|
// ensure a read balance operation is recorded in mvHashmap
|
||||||
|
s.GetBalance(addr)
|
||||||
|
}
|
||||||
|
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
|
stateObject = s.mvRecordWritten(stateObject)
|
||||||
stateObject.AddBalance(amount)
|
stateObject.AddBalance(amount)
|
||||||
|
MVWrite(s, blockstm.NewSubpathKey(addr, BalancePath))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubBalance subtracts amount from the account associated with addr.
|
// SubBalance subtracts amount from the account associated with addr.
|
||||||
func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) {
|
func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) {
|
||||||
stateObject := s.GetOrNewStateObject(addr)
|
stateObject := s.GetOrNewStateObject(addr)
|
||||||
|
|
||||||
|
if s.mvHashmap != nil {
|
||||||
|
// ensure a read balance operation is recorded in mvHashmap
|
||||||
|
s.GetBalance(addr)
|
||||||
|
}
|
||||||
|
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
|
stateObject = s.mvRecordWritten(stateObject)
|
||||||
stateObject.SubBalance(amount)
|
stateObject.SubBalance(amount)
|
||||||
|
MVWrite(s, blockstm.NewSubpathKey(addr, BalancePath))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) SetBalance(addr common.Address, amount *big.Int) {
|
func (s *StateDB) SetBalance(addr common.Address, amount *big.Int) {
|
||||||
stateObject := s.GetOrNewStateObject(addr)
|
stateObject := s.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
|
stateObject = s.mvRecordWritten(stateObject)
|
||||||
stateObject.SetBalance(amount)
|
stateObject.SetBalance(amount)
|
||||||
|
MVWrite(s, blockstm.NewSubpathKey(addr, BalancePath))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) SetNonce(addr common.Address, nonce uint64) {
|
func (s *StateDB) SetNonce(addr common.Address, nonce uint64) {
|
||||||
stateObject := s.GetOrNewStateObject(addr)
|
stateObject := s.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
|
stateObject = s.mvRecordWritten(stateObject)
|
||||||
stateObject.SetNonce(nonce)
|
stateObject.SetNonce(nonce)
|
||||||
|
MVWrite(s, blockstm.NewSubpathKey(addr, NoncePath))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) SetCode(addr common.Address, code []byte) {
|
func (s *StateDB) SetCode(addr common.Address, code []byte) {
|
||||||
stateObject := s.GetOrNewStateObject(addr)
|
stateObject := s.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
|
stateObject = s.mvRecordWritten(stateObject)
|
||||||
stateObject.SetCode(crypto.Keccak256Hash(code), code)
|
stateObject.SetCode(crypto.Keccak256Hash(code), code)
|
||||||
|
MVWrite(s, blockstm.NewSubpathKey(addr, CodePath))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) SetState(addr common.Address, key, value common.Hash) {
|
func (s *StateDB) SetState(addr common.Address, key, value common.Hash) {
|
||||||
stateObject := s.GetOrNewStateObject(addr)
|
stateObject := s.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
|
stateObject = s.mvRecordWritten(stateObject)
|
||||||
stateObject.SetState(s.db, key, value)
|
stateObject.SetState(s.db, key, value)
|
||||||
|
MVWrite(s, blockstm.NewStateKey(addr, key))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -462,6 +808,8 @@ func (s *StateDB) Suicide(addr common.Address) bool {
|
||||||
if stateObject == nil {
|
if stateObject == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stateObject = s.mvRecordWritten(stateObject)
|
||||||
s.journal.append(suicideChange{
|
s.journal.append(suicideChange{
|
||||||
account: &addr,
|
account: &addr,
|
||||||
prev: stateObject.suicided,
|
prev: stateObject.suicided,
|
||||||
|
|
@ -470,6 +818,9 @@ func (s *StateDB) Suicide(addr common.Address) bool {
|
||||||
stateObject.markSuicided()
|
stateObject.markSuicided()
|
||||||
stateObject.data.Balance = new(big.Int)
|
stateObject.data.Balance = new(big.Int)
|
||||||
|
|
||||||
|
MVWrite(s, blockstm.NewSubpathKey(addr, SuicidePath))
|
||||||
|
MVWrite(s, blockstm.NewSubpathKey(addr, BalancePath))
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -553,56 +904,58 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject {
|
||||||
// flag set. This is needed by the state journal to revert to the correct s-
|
// flag set. This is needed by the state journal to revert to the correct s-
|
||||||
// destructed object instead of wiping all knowledge about the state object.
|
// destructed object instead of wiping all knowledge about the state object.
|
||||||
func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
|
func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
|
||||||
// Prefer live objects if any is available
|
return MVRead(s, blockstm.NewAddressKey(addr), nil, func(s *StateDB) *stateObject {
|
||||||
if obj := s.stateObjects[addr]; obj != nil {
|
// Prefer live objects if any is available
|
||||||
return obj
|
if obj := s.stateObjects[addr]; obj != nil {
|
||||||
}
|
return obj
|
||||||
// If no live objects are available, attempt to use snapshots
|
|
||||||
var data *types.StateAccount
|
|
||||||
if s.snap != nil {
|
|
||||||
start := time.Now()
|
|
||||||
acc, err := s.snap.Account(crypto.HashData(s.hasher, addr.Bytes()))
|
|
||||||
if metrics.EnabledExpensive {
|
|
||||||
s.SnapshotAccountReads += time.Since(start)
|
|
||||||
}
|
}
|
||||||
if err == nil {
|
// If no live objects are available, attempt to use snapshots
|
||||||
if acc == nil {
|
var data *types.StateAccount
|
||||||
return nil
|
if s.snap != nil { // nolint
|
||||||
|
start := time.Now()
|
||||||
|
acc, err := s.snap.Account(crypto.HashData(crypto.NewKeccakState(), addr.Bytes()))
|
||||||
|
if metrics.EnabledExpensive {
|
||||||
|
s.SnapshotAccountReads += time.Since(start)
|
||||||
}
|
}
|
||||||
data = &types.StateAccount{
|
if err == nil {
|
||||||
Nonce: acc.Nonce,
|
if acc == nil {
|
||||||
Balance: acc.Balance,
|
return nil
|
||||||
CodeHash: acc.CodeHash,
|
}
|
||||||
Root: common.BytesToHash(acc.Root),
|
data = &types.StateAccount{
|
||||||
}
|
Nonce: acc.Nonce,
|
||||||
if len(data.CodeHash) == 0 {
|
Balance: acc.Balance,
|
||||||
|
CodeHash: acc.CodeHash,
|
||||||
|
Root: common.BytesToHash(acc.Root),
|
||||||
|
}
|
||||||
|
if len(data.CodeHash) == 0 {
|
||||||
data.CodeHash = types.EmptyCodeHash.Bytes()
|
data.CodeHash = types.EmptyCodeHash.Bytes()
|
||||||
}
|
}
|
||||||
if data.Root == (common.Hash{}) {
|
if data.Root == (common.Hash{}) {
|
||||||
data.Root = types.EmptyRootHash
|
data.Root = types.EmptyRootHash
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// If snapshot unavailable or reading from it failed, load from the database
|
||||||
// If snapshot unavailable or reading from it failed, load from the database
|
if data == nil {
|
||||||
if data == nil {
|
start := time.Now()
|
||||||
start := time.Now()
|
|
||||||
var err error
|
var err error
|
||||||
data, err = s.trie.GetAccount(addr)
|
data, err = s.trie.GetAccount(addr)
|
||||||
if metrics.EnabledExpensive {
|
if metrics.EnabledExpensive {
|
||||||
s.AccountReads += time.Since(start)
|
s.AccountReads += time.Since(start)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.setError(fmt.Errorf("getDeleteStateObject (%x) error: %w", addr.Bytes(), err))
|
s.setError(fmt.Errorf("getDeleteStateObject (%x) error: %w", addr.Bytes(), err))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if data == nil {
|
if data == nil {
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
// Insert into the live set
|
||||||
// Insert into the live set
|
obj := newObject(s, addr, *data)
|
||||||
obj := newObject(s, addr, *data)
|
s.setStateObject(obj)
|
||||||
s.setStateObject(obj)
|
return obj
|
||||||
return obj
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) setStateObject(object *stateObject) {
|
func (s *StateDB) setStateObject(object *stateObject) {
|
||||||
|
|
@ -618,6 +971,28 @@ func (s *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
|
||||||
return stateObject
|
return stateObject
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mvRecordWritten checks whether a state object is already present in the current MV writeMap.
|
||||||
|
// If yes, it returns the object directly.
|
||||||
|
// If not, it clones the object and inserts it into the writeMap before returning it.
|
||||||
|
func (s *StateDB) mvRecordWritten(object *stateObject) *stateObject {
|
||||||
|
if s.mvHashmap == nil {
|
||||||
|
return object
|
||||||
|
}
|
||||||
|
|
||||||
|
addrKey := blockstm.NewAddressKey(object.Address())
|
||||||
|
|
||||||
|
if MVWritten(s, addrKey) {
|
||||||
|
return object
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deepcopy is needed to ensure that objects are not written by multiple transactions at the same time, because
|
||||||
|
// the input state object can come from a different transaction.
|
||||||
|
s.setStateObject(object.deepCopy(s))
|
||||||
|
MVWrite(s, addrKey)
|
||||||
|
|
||||||
|
return s.stateObjects[object.Address()]
|
||||||
|
}
|
||||||
|
|
||||||
// createObject creates a new state object. If there is an existing account with
|
// createObject creates a new state object. If there is an existing account with
|
||||||
// the given address, it is overwritten and returned as the second return value.
|
// the given address, it is overwritten and returned as the second return value.
|
||||||
func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
|
func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
|
||||||
|
|
@ -637,6 +1012,8 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
|
||||||
s.journal.append(resetObjectChange{prev: prev, prevdestruct: prevdestruct})
|
s.journal.append(resetObjectChange{prev: prev, prevdestruct: prevdestruct})
|
||||||
}
|
}
|
||||||
s.setStateObject(newobj)
|
s.setStateObject(newobj)
|
||||||
|
|
||||||
|
MVWrite(s, blockstm.NewAddressKey(addr))
|
||||||
if prev != nil && !prev.deleted {
|
if prev != nil && !prev.deleted {
|
||||||
return newobj, prev
|
return newobj, prev
|
||||||
}
|
}
|
||||||
|
|
@ -657,22 +1034,23 @@ func (s *StateDB) CreateAccount(addr common.Address) {
|
||||||
newObj, prev := s.createObject(addr)
|
newObj, prev := s.createObject(addr)
|
||||||
if prev != nil {
|
if prev != nil {
|
||||||
newObj.setBalance(prev.data.Balance)
|
newObj.setBalance(prev.data.Balance)
|
||||||
|
MVWrite(s, blockstm.NewSubpathKey(addr, BalancePath))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error {
|
func (s *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error {
|
||||||
so := db.getStateObject(addr)
|
so := s.getStateObject(addr)
|
||||||
if so == nil {
|
if so == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
tr, err := so.getTrie(db.db)
|
tr, err := so.getTrie(s.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
it := trie.NewIterator(tr.NodeIterator(nil))
|
it := trie.NewIterator(tr.NodeIterator(nil))
|
||||||
|
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
key := common.BytesToHash(db.trie.GetKey(it.Key))
|
key := common.BytesToHash(s.trie.GetKey(it.Key))
|
||||||
if value, dirty := so.dirtyStorage[key]; dirty {
|
if value, dirty := so.dirtyStorage[key]; dirty {
|
||||||
if !cb(key, value) {
|
if !cb(key, value) {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -705,6 +1083,7 @@ func (s *StateDB) Copy() *StateDB {
|
||||||
stateObjectsPending: make(map[common.Address]struct{}, len(s.stateObjectsPending)),
|
stateObjectsPending: make(map[common.Address]struct{}, len(s.stateObjectsPending)),
|
||||||
stateObjectsDirty: make(map[common.Address]struct{}, len(s.journal.dirties)),
|
stateObjectsDirty: make(map[common.Address]struct{}, len(s.journal.dirties)),
|
||||||
stateObjectsDestruct: make(map[common.Address]struct{}, len(s.stateObjectsDestruct)),
|
stateObjectsDestruct: make(map[common.Address]struct{}, len(s.stateObjectsDestruct)),
|
||||||
|
revertedKeys: make(map[blockstm.Key]struct{}),
|
||||||
refund: s.refund,
|
refund: s.refund,
|
||||||
logs: make(map[common.Hash][]*types.Log, len(s.logs)),
|
logs: make(map[common.Hash][]*types.Log, len(s.logs)),
|
||||||
logSize: s.logSize,
|
logSize: s.logSize,
|
||||||
|
|
@ -796,6 +1175,10 @@ func (s *StateDB) Copy() *StateDB {
|
||||||
state.snapStorage[k] = temp
|
state.snapStorage[k] = temp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.mvHashmap != nil {
|
||||||
|
state.mvHashmap = s.mvHashmap
|
||||||
|
}
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,10 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"testing/quick"
|
"testing/quick"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/blockstm"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
)
|
)
|
||||||
|
|
@ -498,6 +501,457 @@ func TestTouchDelete(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMVHashMapReadWriteDelete(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
db := NewDatabase(rawdb.NewMemoryDatabase())
|
||||||
|
mvhm := blockstm.MakeMVHashMap()
|
||||||
|
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
||||||
|
|
||||||
|
states := []*StateDB{s}
|
||||||
|
|
||||||
|
// Create copies of the original state for each transition
|
||||||
|
for i := 1; i <= 4; i++ {
|
||||||
|
sCopy := s.Copy()
|
||||||
|
sCopy.txIndex = i
|
||||||
|
states = append(states, sCopy)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := common.HexToAddress("0x01")
|
||||||
|
key := common.HexToHash("0x01")
|
||||||
|
val := common.HexToHash("0x01")
|
||||||
|
balance := new(big.Int).SetUint64(uint64(100))
|
||||||
|
|
||||||
|
// Tx0 read
|
||||||
|
v := states[0].GetState(addr, key)
|
||||||
|
|
||||||
|
assert.Equal(t, common.Hash{}, v)
|
||||||
|
|
||||||
|
// Tx1 write
|
||||||
|
states[1].GetOrNewStateObject(addr)
|
||||||
|
states[1].SetState(addr, key, val)
|
||||||
|
states[1].SetBalance(addr, balance)
|
||||||
|
states[1].FlushMVWriteSet()
|
||||||
|
|
||||||
|
// Tx1 read
|
||||||
|
v = states[1].GetState(addr, key)
|
||||||
|
b := states[1].GetBalance(addr)
|
||||||
|
|
||||||
|
assert.Equal(t, val, v)
|
||||||
|
assert.Equal(t, balance, b)
|
||||||
|
|
||||||
|
// Tx2 read
|
||||||
|
v = states[2].GetState(addr, key)
|
||||||
|
b = states[2].GetBalance(addr)
|
||||||
|
|
||||||
|
assert.Equal(t, val, v)
|
||||||
|
assert.Equal(t, balance, b)
|
||||||
|
|
||||||
|
// Tx3 delete
|
||||||
|
states[3].Suicide(addr)
|
||||||
|
|
||||||
|
// Within Tx 3, the state should not change before finalize
|
||||||
|
v = states[3].GetState(addr, key)
|
||||||
|
assert.Equal(t, val, v)
|
||||||
|
|
||||||
|
// After finalizing Tx 3, the state will change
|
||||||
|
states[3].Finalise(false)
|
||||||
|
v = states[3].GetState(addr, key)
|
||||||
|
assert.Equal(t, common.Hash{}, v)
|
||||||
|
states[3].FlushMVWriteSet()
|
||||||
|
|
||||||
|
// Tx4 read
|
||||||
|
v = states[4].GetState(addr, key)
|
||||||
|
b = states[4].GetBalance(addr)
|
||||||
|
|
||||||
|
assert.Equal(t, common.Hash{}, v)
|
||||||
|
assert.Equal(t, common.Big0, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMVHashMapRevert(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
db := NewDatabase(rawdb.NewMemoryDatabase())
|
||||||
|
mvhm := blockstm.MakeMVHashMap()
|
||||||
|
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
||||||
|
|
||||||
|
states := []*StateDB{s}
|
||||||
|
|
||||||
|
// Create copies of the original state for each transition
|
||||||
|
for i := 1; i <= 4; i++ {
|
||||||
|
sCopy := s.Copy()
|
||||||
|
sCopy.txIndex = i
|
||||||
|
states = append(states, sCopy)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := common.HexToAddress("0x01")
|
||||||
|
key := common.HexToHash("0x01")
|
||||||
|
val := common.HexToHash("0x01")
|
||||||
|
balance := new(big.Int).SetUint64(uint64(100))
|
||||||
|
|
||||||
|
// Tx0 write
|
||||||
|
states[0].GetOrNewStateObject(addr)
|
||||||
|
states[0].SetState(addr, key, val)
|
||||||
|
states[0].SetBalance(addr, balance)
|
||||||
|
states[0].FlushMVWriteSet()
|
||||||
|
|
||||||
|
// Tx1 perform some ops and then revert
|
||||||
|
snapshot := states[1].Snapshot()
|
||||||
|
states[1].AddBalance(addr, new(big.Int).SetUint64(uint64(100)))
|
||||||
|
states[1].SetState(addr, key, common.HexToHash("0x02"))
|
||||||
|
v := states[1].GetState(addr, key)
|
||||||
|
b := states[1].GetBalance(addr)
|
||||||
|
assert.Equal(t, new(big.Int).SetUint64(uint64(200)), b)
|
||||||
|
assert.Equal(t, common.HexToHash("0x02"), v)
|
||||||
|
|
||||||
|
states[1].Suicide(addr)
|
||||||
|
|
||||||
|
states[1].RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
|
v = states[1].GetState(addr, key)
|
||||||
|
b = states[1].GetBalance(addr)
|
||||||
|
|
||||||
|
assert.Equal(t, val, v)
|
||||||
|
assert.Equal(t, balance, b)
|
||||||
|
states[1].Finalise(false)
|
||||||
|
states[1].FlushMVWriteSet()
|
||||||
|
|
||||||
|
// Tx2 check the state and balance
|
||||||
|
v = states[2].GetState(addr, key)
|
||||||
|
b = states[2].GetBalance(addr)
|
||||||
|
|
||||||
|
assert.Equal(t, val, v)
|
||||||
|
assert.Equal(t, balance, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMVHashMapMarkEstimate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
db := NewDatabase(rawdb.NewMemoryDatabase())
|
||||||
|
mvhm := blockstm.MakeMVHashMap()
|
||||||
|
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
||||||
|
|
||||||
|
states := []*StateDB{s}
|
||||||
|
|
||||||
|
// Create copies of the original state for each transition
|
||||||
|
for i := 1; i <= 4; i++ {
|
||||||
|
sCopy := s.Copy()
|
||||||
|
sCopy.txIndex = i
|
||||||
|
states = append(states, sCopy)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := common.HexToAddress("0x01")
|
||||||
|
key := common.HexToHash("0x01")
|
||||||
|
val := common.HexToHash("0x01")
|
||||||
|
balance := new(big.Int).SetUint64(uint64(100))
|
||||||
|
|
||||||
|
// Tx0 read
|
||||||
|
v := states[0].GetState(addr, key)
|
||||||
|
assert.Equal(t, common.Hash{}, v)
|
||||||
|
|
||||||
|
// Tx0 write
|
||||||
|
states[0].SetState(addr, key, val)
|
||||||
|
v = states[0].GetState(addr, key)
|
||||||
|
assert.Equal(t, val, v)
|
||||||
|
states[0].FlushMVWriteSet()
|
||||||
|
|
||||||
|
// Tx1 write
|
||||||
|
states[1].GetOrNewStateObject(addr)
|
||||||
|
states[1].SetState(addr, key, val)
|
||||||
|
states[1].SetBalance(addr, balance)
|
||||||
|
states[1].FlushMVWriteSet()
|
||||||
|
|
||||||
|
// Tx2 read
|
||||||
|
v = states[2].GetState(addr, key)
|
||||||
|
b := states[2].GetBalance(addr)
|
||||||
|
|
||||||
|
assert.Equal(t, val, v)
|
||||||
|
assert.Equal(t, balance, b)
|
||||||
|
|
||||||
|
// Tx1 mark estimate
|
||||||
|
for _, v := range states[1].MVWriteList() {
|
||||||
|
mvhm.MarkEstimate(v.Path, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r == nil {
|
||||||
|
t.Errorf("The code did not panic")
|
||||||
|
} else {
|
||||||
|
t.Log("Recovered in f", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Tx2 read again should get default (empty) vals because its dependency Tx1 is marked as estimate
|
||||||
|
states[2].GetState(addr, key)
|
||||||
|
states[2].GetBalance(addr)
|
||||||
|
|
||||||
|
// Tx1 read again should get Tx0 vals
|
||||||
|
v = states[1].GetState(addr, key)
|
||||||
|
assert.Equal(t, val, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMVHashMapOverwrite(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
db := NewDatabase(rawdb.NewMemoryDatabase())
|
||||||
|
mvhm := blockstm.MakeMVHashMap()
|
||||||
|
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
||||||
|
|
||||||
|
states := []*StateDB{s}
|
||||||
|
|
||||||
|
// Create copies of the original state for each transition
|
||||||
|
for i := 1; i <= 4; i++ {
|
||||||
|
sCopy := s.Copy()
|
||||||
|
sCopy.txIndex = i
|
||||||
|
states = append(states, sCopy)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := common.HexToAddress("0x01")
|
||||||
|
key := common.HexToHash("0x01")
|
||||||
|
val1 := common.HexToHash("0x01")
|
||||||
|
balance1 := new(big.Int).SetUint64(uint64(100))
|
||||||
|
val2 := common.HexToHash("0x02")
|
||||||
|
balance2 := new(big.Int).SetUint64(uint64(200))
|
||||||
|
|
||||||
|
// Tx0 write
|
||||||
|
states[0].GetOrNewStateObject(addr)
|
||||||
|
states[0].SetState(addr, key, val1)
|
||||||
|
states[0].SetBalance(addr, balance1)
|
||||||
|
states[0].FlushMVWriteSet()
|
||||||
|
|
||||||
|
// Tx1 write
|
||||||
|
states[1].SetState(addr, key, val2)
|
||||||
|
states[1].SetBalance(addr, balance2)
|
||||||
|
v := states[1].GetState(addr, key)
|
||||||
|
b := states[1].GetBalance(addr)
|
||||||
|
states[1].FlushMVWriteSet()
|
||||||
|
|
||||||
|
assert.Equal(t, val2, v)
|
||||||
|
assert.Equal(t, balance2, b)
|
||||||
|
|
||||||
|
// Tx2 read should get Tx1's value
|
||||||
|
v = states[2].GetState(addr, key)
|
||||||
|
b = states[2].GetBalance(addr)
|
||||||
|
|
||||||
|
assert.Equal(t, val2, v)
|
||||||
|
assert.Equal(t, balance2, b)
|
||||||
|
|
||||||
|
// Tx1 delete
|
||||||
|
for _, v := range states[1].writeMap {
|
||||||
|
mvhm.Delete(v.Path, 1)
|
||||||
|
|
||||||
|
states[1].writeMap = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tx2 read should get Tx0's value
|
||||||
|
v = states[2].GetState(addr, key)
|
||||||
|
b = states[2].GetBalance(addr)
|
||||||
|
|
||||||
|
assert.Equal(t, val1, v)
|
||||||
|
assert.Equal(t, balance1, b)
|
||||||
|
|
||||||
|
// Tx1 read should get Tx0's value
|
||||||
|
v = states[1].GetState(addr, key)
|
||||||
|
b = states[1].GetBalance(addr)
|
||||||
|
|
||||||
|
assert.Equal(t, val1, v)
|
||||||
|
assert.Equal(t, balance1, b)
|
||||||
|
|
||||||
|
// Tx0 delete
|
||||||
|
for _, v := range states[0].writeMap {
|
||||||
|
mvhm.Delete(v.Path, 0)
|
||||||
|
|
||||||
|
states[0].writeMap = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tx2 read again should get default vals
|
||||||
|
v = states[2].GetState(addr, key)
|
||||||
|
b = states[2].GetBalance(addr)
|
||||||
|
|
||||||
|
assert.Equal(t, common.Hash{}, v)
|
||||||
|
assert.Equal(t, common.Big0, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMVHashMapWriteNoConflict(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
db := NewDatabase(rawdb.NewMemoryDatabase())
|
||||||
|
mvhm := blockstm.MakeMVHashMap()
|
||||||
|
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
||||||
|
|
||||||
|
states := []*StateDB{s}
|
||||||
|
|
||||||
|
// Create copies of the original state for each transition
|
||||||
|
for i := 1; i <= 4; i++ {
|
||||||
|
sCopy := s.Copy()
|
||||||
|
sCopy.txIndex = i
|
||||||
|
states = append(states, sCopy)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := common.HexToAddress("0x01")
|
||||||
|
key1 := common.HexToHash("0x01")
|
||||||
|
key2 := common.HexToHash("0x02")
|
||||||
|
val1 := common.HexToHash("0x01")
|
||||||
|
balance1 := new(big.Int).SetUint64(uint64(100))
|
||||||
|
val2 := common.HexToHash("0x02")
|
||||||
|
|
||||||
|
// Tx0 write
|
||||||
|
states[0].GetOrNewStateObject(addr)
|
||||||
|
states[0].FlushMVWriteSet()
|
||||||
|
|
||||||
|
// Tx2 write
|
||||||
|
states[2].SetState(addr, key2, val2)
|
||||||
|
states[2].FlushMVWriteSet()
|
||||||
|
|
||||||
|
// Tx1 write
|
||||||
|
tx1Snapshot := states[1].Snapshot()
|
||||||
|
states[1].SetState(addr, key1, val1)
|
||||||
|
states[1].SetBalance(addr, balance1)
|
||||||
|
states[1].FlushMVWriteSet()
|
||||||
|
|
||||||
|
// Tx1 read
|
||||||
|
assert.Equal(t, val1, states[1].GetState(addr, key1))
|
||||||
|
assert.Equal(t, balance1, states[1].GetBalance(addr))
|
||||||
|
// Tx1 should see empty value in key2
|
||||||
|
assert.Equal(t, common.Hash{}, states[1].GetState(addr, key2))
|
||||||
|
|
||||||
|
// Tx2 read
|
||||||
|
assert.Equal(t, val2, states[2].GetState(addr, key2))
|
||||||
|
// Tx2 should see values written by Tx1
|
||||||
|
assert.Equal(t, val1, states[2].GetState(addr, key1))
|
||||||
|
assert.Equal(t, balance1, states[2].GetBalance(addr))
|
||||||
|
|
||||||
|
// Tx3 read
|
||||||
|
assert.Equal(t, val1, states[3].GetState(addr, key1))
|
||||||
|
assert.Equal(t, val2, states[3].GetState(addr, key2))
|
||||||
|
assert.Equal(t, balance1, states[3].GetBalance(addr))
|
||||||
|
|
||||||
|
// Tx2 delete
|
||||||
|
for _, v := range states[2].writeMap {
|
||||||
|
mvhm.Delete(v.Path, 2)
|
||||||
|
|
||||||
|
states[2].writeMap = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, val1, states[3].GetState(addr, key1))
|
||||||
|
assert.Equal(t, balance1, states[3].GetBalance(addr))
|
||||||
|
assert.Equal(t, common.Hash{}, states[3].GetState(addr, key2))
|
||||||
|
|
||||||
|
// Tx1 revert
|
||||||
|
states[1].RevertToSnapshot(tx1Snapshot)
|
||||||
|
states[1].FlushMVWriteSet()
|
||||||
|
|
||||||
|
assert.Equal(t, common.Hash{}, states[3].GetState(addr, key1))
|
||||||
|
assert.Equal(t, common.Hash{}, states[3].GetState(addr, key2))
|
||||||
|
assert.Equal(t, common.Big0, states[3].GetBalance(addr))
|
||||||
|
|
||||||
|
// Tx1 delete
|
||||||
|
for _, v := range states[1].writeMap {
|
||||||
|
mvhm.Delete(v.Path, 1)
|
||||||
|
|
||||||
|
states[1].writeMap = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, common.Hash{}, states[3].GetState(addr, key1))
|
||||||
|
assert.Equal(t, common.Hash{}, states[3].GetState(addr, key2))
|
||||||
|
assert.Equal(t, common.Big0, states[3].GetBalance(addr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApplyMVWriteSet(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
db := NewDatabase(rawdb.NewMemoryDatabase())
|
||||||
|
mvhm := blockstm.MakeMVHashMap()
|
||||||
|
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
||||||
|
|
||||||
|
sClean := s.Copy()
|
||||||
|
sClean.mvHashmap = nil
|
||||||
|
|
||||||
|
sSingleProcess := sClean.Copy()
|
||||||
|
|
||||||
|
states := []*StateDB{s}
|
||||||
|
|
||||||
|
// Create copies of the original state for each transition
|
||||||
|
for i := 1; i <= 4; i++ {
|
||||||
|
sCopy := s.Copy()
|
||||||
|
sCopy.txIndex = i
|
||||||
|
states = append(states, sCopy)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr1 := common.HexToAddress("0x01")
|
||||||
|
addr2 := common.HexToAddress("0x02")
|
||||||
|
addr3 := common.HexToAddress("0x03")
|
||||||
|
key1 := common.HexToHash("0x01")
|
||||||
|
key2 := common.HexToHash("0x02")
|
||||||
|
val1 := common.HexToHash("0x01")
|
||||||
|
balance1 := new(big.Int).SetUint64(uint64(100))
|
||||||
|
val2 := common.HexToHash("0x02")
|
||||||
|
balance2 := new(big.Int).SetUint64(uint64(200))
|
||||||
|
code := []byte{1, 2, 3}
|
||||||
|
|
||||||
|
// Tx0 write
|
||||||
|
states[0].GetOrNewStateObject(addr1)
|
||||||
|
states[0].SetState(addr1, key1, val1)
|
||||||
|
states[0].SetBalance(addr1, balance1)
|
||||||
|
states[0].SetState(addr2, key2, val2)
|
||||||
|
states[0].GetOrNewStateObject(addr3)
|
||||||
|
states[0].Finalise(true)
|
||||||
|
states[0].FlushMVWriteSet()
|
||||||
|
|
||||||
|
sSingleProcess.GetOrNewStateObject(addr1)
|
||||||
|
sSingleProcess.SetState(addr1, key1, val1)
|
||||||
|
sSingleProcess.SetBalance(addr1, balance1)
|
||||||
|
sSingleProcess.SetState(addr2, key2, val2)
|
||||||
|
sSingleProcess.GetOrNewStateObject(addr3)
|
||||||
|
|
||||||
|
sClean.ApplyMVWriteSet(states[0].MVWriteList())
|
||||||
|
|
||||||
|
assert.Equal(t, sSingleProcess.IntermediateRoot(true), sClean.IntermediateRoot(true))
|
||||||
|
|
||||||
|
// Tx1 write
|
||||||
|
states[1].SetState(addr1, key2, val2)
|
||||||
|
states[1].SetBalance(addr1, balance2)
|
||||||
|
states[1].SetNonce(addr1, 1)
|
||||||
|
states[1].Finalise(true)
|
||||||
|
states[1].FlushMVWriteSet()
|
||||||
|
|
||||||
|
sSingleProcess.SetState(addr1, key2, val2)
|
||||||
|
sSingleProcess.SetBalance(addr1, balance2)
|
||||||
|
sSingleProcess.SetNonce(addr1, 1)
|
||||||
|
|
||||||
|
sClean.ApplyMVWriteSet(states[1].MVWriteList())
|
||||||
|
|
||||||
|
assert.Equal(t, sSingleProcess.IntermediateRoot(true), sClean.IntermediateRoot(true))
|
||||||
|
|
||||||
|
// Tx2 write
|
||||||
|
states[2].SetState(addr1, key1, val2)
|
||||||
|
states[2].SetBalance(addr1, balance2)
|
||||||
|
states[2].SetNonce(addr1, 2)
|
||||||
|
states[2].Finalise(true)
|
||||||
|
states[2].FlushMVWriteSet()
|
||||||
|
|
||||||
|
sSingleProcess.SetState(addr1, key1, val2)
|
||||||
|
sSingleProcess.SetBalance(addr1, balance2)
|
||||||
|
sSingleProcess.SetNonce(addr1, 2)
|
||||||
|
|
||||||
|
sClean.ApplyMVWriteSet(states[2].MVWriteList())
|
||||||
|
|
||||||
|
assert.Equal(t, sSingleProcess.IntermediateRoot(true), sClean.IntermediateRoot(true))
|
||||||
|
|
||||||
|
// Tx3 write
|
||||||
|
states[3].Suicide(addr2)
|
||||||
|
states[3].SetCode(addr1, code)
|
||||||
|
states[3].Finalise(true)
|
||||||
|
states[3].FlushMVWriteSet()
|
||||||
|
|
||||||
|
sSingleProcess.Suicide(addr2)
|
||||||
|
sSingleProcess.SetCode(addr1, code)
|
||||||
|
|
||||||
|
sClean.ApplyMVWriteSet(states[3].MVWriteList())
|
||||||
|
|
||||||
|
assert.Equal(t, sSingleProcess.IntermediateRoot(true), sClean.IntermediateRoot(true))
|
||||||
|
}
|
||||||
|
|
||||||
// TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy.
|
// TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy.
|
||||||
// See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512
|
// See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512
|
||||||
func TestCopyOfCopy(t *testing.T) {
|
func TestCopyOfCopy(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen
|
||||||
// Process returns the receipts and logs accumulated during the process and
|
// Process returns the receipts and logs accumulated during the process and
|
||||||
// returns the amount of gas that was used in the process. If any of the
|
// returns the amount of gas that was used in the process. If any of the
|
||||||
// transactions failed to execute due to insufficient gas it will return an error.
|
// transactions failed to execute due to insufficient gas it will return an error.
|
||||||
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
|
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config, interruptCtx context.Context) (types.Receipts, []*types.Log, uint64, error) {
|
||||||
var (
|
var (
|
||||||
receipts types.Receipts
|
receipts types.Receipts
|
||||||
usedGas = new(uint64)
|
usedGas = new(uint64)
|
||||||
|
|
@ -75,12 +75,19 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
|
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
|
||||||
// Iterate over and process the individual transactions
|
// Iterate over and process the individual transactions
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
|
if interruptCtx != nil {
|
||||||
|
select {
|
||||||
|
case <-interruptCtx.Done():
|
||||||
|
return nil, nil, 0, interruptCtx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
msg, err := TransactionToMessage(tx, types.MakeSigner(p.config, header.Number), header.BaseFee)
|
msg, err := TransactionToMessage(tx, types.MakeSigner(p.config, header.Number), header.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||||
}
|
}
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
statedb.SetTxContext(tx.Hash(), i)
|
||||||
receipt, err := applyTransaction(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, nil)
|
receipt, err := applyTransaction(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, interruptCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||||
}
|
}
|
||||||
|
|
@ -89,6 +96,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
}
|
}
|
||||||
// Fail if Shanghai not enabled and len(withdrawals) is non-zero.
|
// Fail if Shanghai not enabled and len(withdrawals) is non-zero.
|
||||||
withdrawals := block.Withdrawals()
|
withdrawals := block.Withdrawals()
|
||||||
|
// TODO marcello IsShanghai
|
||||||
if len(withdrawals) > 0 && !p.config.IsShanghai(block.Time()) {
|
if len(withdrawals) > 0 && !p.config.IsShanghai(block.Time()) {
|
||||||
return nil, nil, 0, fmt.Errorf("withdrawals before shanghai")
|
return nil, nil, 0, fmt.Errorf("withdrawals before shanghai")
|
||||||
}
|
}
|
||||||
|
|
@ -104,12 +112,51 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta
|
||||||
txContext := NewEVMTxContext(msg)
|
txContext := NewEVMTxContext(msg)
|
||||||
evm.Reset(txContext, statedb)
|
evm.Reset(txContext, statedb)
|
||||||
|
|
||||||
// Apply the transaction to the current state (included in the env).
|
var result *ExecutionResult
|
||||||
result, err := ApplyMessage(evm, msg, gp, interruptCtx)
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
backupMVHashMap := statedb.GetMVHashmap()
|
||||||
|
|
||||||
|
// pause recording read and write
|
||||||
|
statedb.SetMVHashmap(nil)
|
||||||
|
|
||||||
|
coinbaseBalance := statedb.GetBalance(evm.Context.Coinbase)
|
||||||
|
|
||||||
|
// resume recording read and write
|
||||||
|
statedb.SetMVHashmap(backupMVHashMap)
|
||||||
|
|
||||||
|
result, err = ApplyMessageNoFeeBurnOrTip(evm, *msg, gp, interruptCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stop recording read and write
|
||||||
|
statedb.SetMVHashmap(nil)
|
||||||
|
|
||||||
|
if evm.ChainConfig().IsLondon(blockNumber) {
|
||||||
|
statedb.AddBalance(result.BurntContractAddress, result.FeeBurnt)
|
||||||
|
}
|
||||||
|
|
||||||
|
statedb.AddBalance(evm.Context.Coinbase, result.FeeTipped)
|
||||||
|
output1 := new(big.Int).SetBytes(result.SenderInitBalance.Bytes())
|
||||||
|
output2 := new(big.Int).SetBytes(coinbaseBalance.Bytes())
|
||||||
|
|
||||||
|
// Deprecating transfer log and will be removed in future fork. PLEASE DO NOT USE this transfer log going forward. Parameters won't get updated as expected going forward with EIP1559
|
||||||
|
// add transfer log
|
||||||
|
AddFeeTransferLog(
|
||||||
|
statedb,
|
||||||
|
|
||||||
|
msg.From,
|
||||||
|
evm.Context.Coinbase,
|
||||||
|
|
||||||
|
result.FeeTipped,
|
||||||
|
result.SenderInitBalance,
|
||||||
|
coinbaseBalance,
|
||||||
|
output1.Sub(output1, result.FeeTipped),
|
||||||
|
output2.Add(output2, result.FeeTipped),
|
||||||
|
)
|
||||||
|
|
||||||
if result.Err == vm.ErrInterrupt {
|
if result.Err == vm.ErrInterrupt {
|
||||||
return nil, result.Err
|
return nil, result.Err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -247,27 +247,32 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
blockchain, _ = NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
blockchain, _ = NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
|
parallelBlockchain, _ = NewParallelBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{ParallelEnable: true, ParallelSpeculativeProcesses: 8}, nil, nil, nil)
|
||||||
)
|
)
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
for i, tt := range []struct {
|
defer parallelBlockchain.Stop()
|
||||||
txs []*types.Transaction
|
|
||||||
want string
|
for _, bc := range []*BlockChain{blockchain, parallelBlockchain} {
|
||||||
}{
|
for i, tt := range []struct {
|
||||||
{ // ErrTxTypeNotSupported
|
txs []*types.Transaction
|
||||||
txs: []*types.Transaction{
|
want string
|
||||||
mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)),
|
}{
|
||||||
|
{ // ErrTxTypeNotSupported
|
||||||
|
txs: []*types.Transaction{
|
||||||
|
mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)),
|
||||||
|
},
|
||||||
|
want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: transaction type not supported",
|
||||||
},
|
},
|
||||||
want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: transaction type not supported",
|
} {
|
||||||
},
|
block := GenerateBadBlock(gspec.ToBlock(), ethash.NewFaker(), tt.txs, gspec.Config)
|
||||||
} {
|
_, err := bc.InsertChain(types.Blocks{block})
|
||||||
block := GenerateBadBlock(gspec.ToBlock(), ethash.NewFaker(), tt.txs, gspec.Config)
|
if err == nil {
|
||||||
_, err := blockchain.InsertChain(types.Blocks{block})
|
t.Fatal("block imported without errors")
|
||||||
if err == nil {
|
}
|
||||||
t.Fatal("block imported without errors")
|
if have, want := err.Error(), tt.want; have != want {
|
||||||
}
|
t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
|
||||||
if have, want := err.Error(), tt.want; have != want {
|
}
|
||||||
t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -286,27 +291,32 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
blockchain, _ = NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
blockchain, _ = NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
|
parallelBlockchain, _ = NewParallelBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{ParallelEnable: true, ParallelSpeculativeProcesses: 8}, nil, nil, nil)
|
||||||
)
|
)
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
for i, tt := range []struct {
|
defer parallelBlockchain.Stop()
|
||||||
txs []*types.Transaction
|
|
||||||
want string
|
for _, bc := range []*BlockChain{blockchain, parallelBlockchain} {
|
||||||
}{
|
for i, tt := range []struct {
|
||||||
{ // ErrSenderNoEOA
|
txs []*types.Transaction
|
||||||
txs: []*types.Transaction{
|
want string
|
||||||
mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)),
|
}{
|
||||||
|
{ // ErrSenderNoEOA
|
||||||
|
txs: []*types.Transaction{
|
||||||
|
mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)),
|
||||||
|
},
|
||||||
|
want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1",
|
||||||
},
|
},
|
||||||
want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1",
|
} {
|
||||||
},
|
block := GenerateBadBlock(gspec.ToBlock(), ethash.NewFaker(), tt.txs, gspec.Config)
|
||||||
} {
|
_, err := bc.InsertChain(types.Blocks{block})
|
||||||
block := GenerateBadBlock(gspec.ToBlock(), ethash.NewFaker(), tt.txs, gspec.Config)
|
if err == nil {
|
||||||
_, err := blockchain.InsertChain(types.Blocks{block})
|
t.Fatal("block imported without errors")
|
||||||
if err == nil {
|
}
|
||||||
t.Fatal("block imported without errors")
|
if have, want := err.Error(), tt.want; have != want {
|
||||||
}
|
t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
|
||||||
if have, want := err.Error(), tt.want; have != want {
|
}
|
||||||
t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -344,36 +354,42 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
genesis = gspec.MustCommit(db)
|
genesis = gspec.MustCommit(db)
|
||||||
blockchain, _ = NewBlockChain(db, nil, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil, nil, nil)
|
blockchain, _ = NewBlockChain(db, nil, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil, nil, nil)
|
||||||
tooBigInitCode = [params.MaxInitCodeSize + 1]byte{}
|
parallelBlockchain, _ = NewParallelBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{ParallelEnable: true, ParallelSpeculativeProcesses: 8}, nil, nil, nil)
|
||||||
smallInitCode = [320]byte{}
|
tooBigInitCode = [params.MaxInitCodeSize + 1]byte{}
|
||||||
|
smallInitCode = [320]byte{}
|
||||||
)
|
)
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
for i, tt := range []struct {
|
defer parallelBlockchain.Stop()
|
||||||
txs []*types.Transaction
|
|
||||||
want string
|
for _, bc := range []*BlockChain{blockchain, parallelBlockchain} {
|
||||||
}{
|
|
||||||
{ // ErrMaxInitCodeSizeExceeded
|
for i, tt := range []struct {
|
||||||
txs: []*types.Transaction{
|
txs []*types.Transaction
|
||||||
mkDynamicCreationTx(0, 500000, common.Big0, misc.CalcBaseFee(config, genesis.Header()), tooBigInitCode[:]),
|
want string
|
||||||
|
}{
|
||||||
|
{ // ErrMaxInitCodeSizeExceeded
|
||||||
|
txs: []*types.Transaction{
|
||||||
|
mkDynamicCreationTx(0, 500000, common.Big0, misc.CalcBaseFee(config, genesis.Header()), tooBigInitCode[:]),
|
||||||
|
},
|
||||||
|
want: "could not apply tx 0 [0x832b54a6c3359474a9f504b1003b2cc1b6fcaa18e4ef369eb45b5d40dad6378f]: max initcode size exceeded: code size 49153 limit 49152",
|
||||||
},
|
},
|
||||||
want: "could not apply tx 0 [0x832b54a6c3359474a9f504b1003b2cc1b6fcaa18e4ef369eb45b5d40dad6378f]: max initcode size exceeded: code size 49153 limit 49152",
|
{ // ErrIntrinsicGas: Not enough gas to cover init code
|
||||||
},
|
txs: []*types.Transaction{
|
||||||
{ // ErrIntrinsicGas: Not enough gas to cover init code
|
mkDynamicCreationTx(0, 54299, common.Big0, misc.CalcBaseFee(config, genesis.Header()), smallInitCode[:]),
|
||||||
txs: []*types.Transaction{
|
},
|
||||||
mkDynamicCreationTx(0, 54299, common.Big0, misc.CalcBaseFee(config, genesis.Header()), smallInitCode[:]),
|
want: "could not apply tx 0 [0x39b7436cb432d3662a25626474282c5c4c1a213326fd87e4e18a91477bae98b2]: intrinsic gas too low: have 54299, want 54300",
|
||||||
},
|
},
|
||||||
want: "could not apply tx 0 [0x39b7436cb432d3662a25626474282c5c4c1a213326fd87e4e18a91477bae98b2]: intrinsic gas too low: have 54299, want 54300",
|
} {
|
||||||
},
|
block := GenerateBadBlock(genesis, beacon.New(ethash.NewFaker()), tt.txs, gspec.Config)
|
||||||
} {
|
_, err := bc.InsertChain(types.Blocks{block})
|
||||||
block := GenerateBadBlock(genesis, beacon.New(ethash.NewFaker()), tt.txs, gspec.Config)
|
if err == nil {
|
||||||
_, err := blockchain.InsertChain(types.Blocks{block})
|
t.Fatal("block imported without errors")
|
||||||
if err == nil {
|
}
|
||||||
t.Fatal("block imported without errors")
|
if have, want := err.Error(), tt.want; have != want {
|
||||||
}
|
t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
|
||||||
if have, want := err.Error(), tt.want; have != want {
|
}
|
||||||
t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,10 @@ type ExecutionResult struct {
|
||||||
UsedGas uint64 // Total used gas but include the refunded gas
|
UsedGas uint64 // Total used gas but include the refunded gas
|
||||||
Err error // Any error encountered during the execution(listed in core/vm/errors.go)
|
Err error // Any error encountered during the execution(listed in core/vm/errors.go)
|
||||||
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
|
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
|
||||||
|
SenderInitBalance *big.Int
|
||||||
|
FeeBurnt *big.Int
|
||||||
|
BurntContractAddress common.Address
|
||||||
|
FeeTipped *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unwrap returns the internal evm error which allows us for further
|
// Unwrap returns the internal evm error which allows us for further
|
||||||
|
|
@ -177,6 +181,13 @@ func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool, interruptCtx context.C
|
||||||
return NewStateTransition(evm, msg, gp).TransitionDb(interruptCtx)
|
return NewStateTransition(evm, msg, gp).TransitionDb(interruptCtx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ApplyMessageNoFeeBurnOrTip(evm *vm.EVM, msg Message, gp *GasPool, interruptCtx context.Context) (*ExecutionResult, error) {
|
||||||
|
st := NewStateTransition(evm, msg, gp)
|
||||||
|
st.noFeeBurnAndTip = true
|
||||||
|
|
||||||
|
return st.TransitionDb(interruptCtx)
|
||||||
|
}
|
||||||
|
|
||||||
// StateTransition represents a state transition.
|
// StateTransition represents a state transition.
|
||||||
//
|
//
|
||||||
// == The State Transitioning Model
|
// == The State Transitioning Model
|
||||||
|
|
@ -311,8 +322,10 @@ func (st *StateTransition) preCheck() error {
|
||||||
// nil evm execution result.
|
// nil evm execution result.
|
||||||
func (st *StateTransition) TransitionDb(interruptCtx context.Context) (*ExecutionResult, error) {
|
func (st *StateTransition) TransitionDb(interruptCtx context.Context) (*ExecutionResult, error) {
|
||||||
input1 := st.state.GetBalance(st.msg.From)
|
input1 := st.state.GetBalance(st.msg.From)
|
||||||
input2 := st.state.GetBalance(st.evm.Context.Coinbase)
|
var input2 *big.Int
|
||||||
|
if !st.noFeeBurnAndTip {
|
||||||
|
input2 := st.state.GetBalance(st.evm.Context.Coinbase)
|
||||||
|
}
|
||||||
// First check this message satisfies all consensus rules before
|
// First check this message satisfies all consensus rules before
|
||||||
// applying the message. The rules include these clauses
|
// applying the message. The rules include these clauses
|
||||||
//
|
//
|
||||||
|
|
@ -404,34 +417,47 @@ func (st *StateTransition) TransitionDb(interruptCtx context.Context) (*Executio
|
||||||
|
|
||||||
amount := new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip)
|
amount := new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip)
|
||||||
|
|
||||||
|
var burnAmount *big.Int
|
||||||
|
|
||||||
|
var burntContractAddress common.Address
|
||||||
|
|
||||||
if rules.IsLondon {
|
if rules.IsLondon {
|
||||||
burntContractAddress := common.HexToAddress(st.evm.ChainConfig().Bor.CalculateBurntContract(st.evm.Context.BlockNumber.Uint64()))
|
burntContractAddress := common.HexToAddress(st.evm.ChainConfig().Bor.CalculateBurntContract(st.evm.Context.BlockNumber.Uint64()))
|
||||||
burnAmount := new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.evm.Context.BaseFee)
|
burnAmount := new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.evm.Context.BaseFee)
|
||||||
st.state.AddBalance(burntContractAddress, burnAmount)
|
if !st.noFeeBurnAndTip {
|
||||||
|
st.state.AddBalance(burntContractAddress, burnAmount)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
st.state.AddBalance(st.evm.Context.Coinbase, amount)
|
if !st.noFeeBurnAndTip {
|
||||||
output1 := new(big.Int).SetBytes(input1.Bytes())
|
st.state.AddBalance(st.evm.Context.Coinbase, amount)
|
||||||
output2 := new(big.Int).SetBytes(input2.Bytes())
|
output1 := new(big.Int).SetBytes(input1.Bytes())
|
||||||
|
output2 := new(big.Int).SetBytes(input2.Bytes())
|
||||||
|
|
||||||
// Deprecating transfer log and will be removed in future fork. PLEASE DO NOT USE this transfer log going forward. Parameters won't get updated as expected going forward with EIP1559
|
// Deprecating transfer log and will be removed in future fork. PLEASE DO NOT USE this transfer log going forward. Parameters won't get updated as expected going forward with EIP1559
|
||||||
// add transfer log
|
// add transfer log
|
||||||
AddFeeTransferLog(
|
AddFeeTransferLog(
|
||||||
st.state,
|
st.state,
|
||||||
|
|
||||||
msg.From,
|
msg.From,
|
||||||
st.evm.Context.Coinbase,
|
st.evm.Context.Coinbase,
|
||||||
|
|
||||||
|
amount,
|
||||||
|
input1,
|
||||||
|
input2,
|
||||||
|
output1.Sub(output1, amount),
|
||||||
|
output2.Add(output2, amount),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
amount,
|
|
||||||
input1,
|
|
||||||
input2,
|
|
||||||
output1.Sub(output1, amount),
|
|
||||||
output2.Add(output2, amount),
|
|
||||||
)
|
|
||||||
|
|
||||||
return &ExecutionResult{
|
return &ExecutionResult{
|
||||||
UsedGas: st.gasUsed(),
|
UsedGas: st.gasUsed(),
|
||||||
Err: vmerr,
|
Err: vmerr,
|
||||||
ReturnData: ret,
|
ReturnData: ret,
|
||||||
|
SenderInitBalance: input1,
|
||||||
|
FeeBurnt: burnAmount,
|
||||||
|
BurntContractAddress: burntContractAddress,
|
||||||
|
FeeTipped: amount,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
|
@ -49,5 +50,5 @@ type Processor interface {
|
||||||
// Process processes the state changes according to the Ethereum rules by running
|
// Process processes the state changes according to the Ethereum rules by running
|
||||||
// the transaction messages using the statedb and applying any rewards to both
|
// the transaction messages using the statedb and applying any rewards to both
|
||||||
// the processor (coinbase) and any included uncles.
|
// the processor (coinbase) and any included uncles.
|
||||||
Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error)
|
Process(block *types.Block, statedb *state.StateDB, cfg vm.Config, interruptCtx context.Context) (types.Receipts, []*types.Log, uint64, error)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,11 @@ type Header struct {
|
||||||
// ExcessDataGas was added by EIP-4844 and is ignored in legacy headers.
|
// ExcessDataGas was added by EIP-4844 and is ignored in legacy headers.
|
||||||
ExcessDataGas *big.Int `json:"excessDataGas" rlp:"optional"`
|
ExcessDataGas *big.Int `json:"excessDataGas" rlp:"optional"`
|
||||||
|
|
||||||
|
// length of TxDependency -> n (n = number of transactions in the block)
|
||||||
|
// length of TxDependency[i] -> k (k = a whole number)
|
||||||
|
// k elements in TxDependency[i] -> transaction indexes on which transaction i is dependent on
|
||||||
|
TxDependency [][]uint64 `json:"txDependency" rlp:"optional"`
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO (MariusVanDerWijden) Add this field once needed
|
TODO (MariusVanDerWijden) Add this field once needed
|
||||||
// Random was added during the merge and contains the BeaconState randomness
|
// Random was added during the merge and contains the BeaconState randomness
|
||||||
|
|
@ -285,6 +290,15 @@ func CopyHeader(h *Header) *Header {
|
||||||
cpy.WithdrawalsHash = new(common.Hash)
|
cpy.WithdrawalsHash = new(common.Hash)
|
||||||
*cpy.WithdrawalsHash = *h.WithdrawalsHash
|
*cpy.WithdrawalsHash = *h.WithdrawalsHash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(h.TxDependency) > 0 {
|
||||||
|
cpy.TxDependency = make([][]uint64, len(h.TxDependency))
|
||||||
|
|
||||||
|
for i, dep := range h.TxDependency {
|
||||||
|
cpy.TxDependency[i] = make([]uint64, len(dep))
|
||||||
|
copy(cpy.TxDependency[i], dep)
|
||||||
|
}
|
||||||
|
}
|
||||||
return &cpy
|
return &cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -341,6 +355,7 @@ func (b *Block) TxHash() common.Hash { return b.header.TxHash }
|
||||||
func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash }
|
func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash }
|
||||||
func (b *Block) UncleHash() common.Hash { return b.header.UncleHash }
|
func (b *Block) UncleHash() common.Hash { return b.header.UncleHash }
|
||||||
func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) }
|
func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) }
|
||||||
|
func (b *Block) TxDependency() [][]uint64 { return b.header.TxDependency }
|
||||||
|
|
||||||
func (b *Block) BaseFee() *big.Int {
|
func (b *Block) BaseFee() *big.Int {
|
||||||
if b.header.BaseFee == nil {
|
if b.header.BaseFee == nil {
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,51 @@ func TestBlockEncoding(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTxDependencyBlockEncoding(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
blockEnc := common.FromHex("f90268f90201a083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c480c6c20201c20180f861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1c0")
|
||||||
|
|
||||||
|
var block Block
|
||||||
|
|
||||||
|
if err := rlp.DecodeBytes(blockEnc, &block); err != nil {
|
||||||
|
t.Fatal("decode error: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
check := func(f string, got, want interface{}) {
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("%s mismatch: got %v, want %v", f, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check("Difficulty", block.Difficulty(), big.NewInt(131072))
|
||||||
|
check("GasLimit", block.GasLimit(), uint64(3141592))
|
||||||
|
check("GasUsed", block.GasUsed(), uint64(21000))
|
||||||
|
check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1"))
|
||||||
|
check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498"))
|
||||||
|
check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
|
||||||
|
check("Hash", block.Hash(), common.HexToHash("0xc6d8dc8995c0a4374bb9f87bd0dd8c0761e6e026a71edbfed5e961c9e55dbd6a"))
|
||||||
|
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
|
||||||
|
check("Time", block.Time(), uint64(1426516743))
|
||||||
|
check("Size", block.Size(), common.StorageSize(len(blockEnc)))
|
||||||
|
check("TxDependency", block.TxDependency(), [][]uint64{{2, 1}, {1, 0}})
|
||||||
|
|
||||||
|
tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), 50000, big.NewInt(10), nil)
|
||||||
|
tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100"))
|
||||||
|
|
||||||
|
check("len(Transactions)", len(block.Transactions()), 1)
|
||||||
|
check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash())
|
||||||
|
ourBlockEnc, err := rlp.EncodeToBytes(&block)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("encode error: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(ourBlockEnc, blockEnc) {
|
||||||
|
t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestEIP1559BlockEncoding(t *testing.T) {
|
func TestEIP1559BlockEncoding(t *testing.T) {
|
||||||
blockEnc := common.FromHex("f9030bf901fea083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4843b9aca00f90106f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1b8a302f8a0018080843b9aca008301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8c0")
|
blockEnc := common.FromHex("f9030bf901fea083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4843b9aca00f90106f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1b8a302f8a0018080843b9aca008301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8c0")
|
||||||
var block Block
|
var block Block
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
||||||
Nonce BlockNonce `json:"nonce"`
|
Nonce BlockNonce `json:"nonce"`
|
||||||
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
||||||
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||||
|
TxDependency [][]uint64 `json:"txDependency" rlp:"optional"`
|
||||||
Hash common.Hash `json:"hash"`
|
Hash common.Hash `json:"hash"`
|
||||||
}
|
}
|
||||||
var enc Header
|
var enc Header
|
||||||
|
|
@ -53,6 +54,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
||||||
enc.Nonce = h.Nonce
|
enc.Nonce = h.Nonce
|
||||||
enc.BaseFee = (*hexutil.Big)(h.BaseFee)
|
enc.BaseFee = (*hexutil.Big)(h.BaseFee)
|
||||||
enc.WithdrawalsHash = h.WithdrawalsHash
|
enc.WithdrawalsHash = h.WithdrawalsHash
|
||||||
|
enc.TxDependency = h.TxDependency
|
||||||
enc.Hash = h.Hash()
|
enc.Hash = h.Hash()
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
@ -77,6 +79,7 @@ func (h *Header) UnmarshalJSON(input []byte) error {
|
||||||
Nonce *BlockNonce `json:"nonce"`
|
Nonce *BlockNonce `json:"nonce"`
|
||||||
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
||||||
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||||
|
TxDependency [][]uint64 `json:"txDependency" rlp:"optional"`
|
||||||
}
|
}
|
||||||
var dec Header
|
var dec Header
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
|
@ -145,5 +148,8 @@ func (h *Header) UnmarshalJSON(input []byte) error {
|
||||||
if dec.WithdrawalsHash != nil {
|
if dec.WithdrawalsHash != nil {
|
||||||
h.WithdrawalsHash = dec.WithdrawalsHash
|
h.WithdrawalsHash = dec.WithdrawalsHash
|
||||||
}
|
}
|
||||||
|
if dec.TxDependency != nil {
|
||||||
|
h.TxDependency = dec.TxDependency
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,8 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
|
||||||
w.WriteBytes(obj.Nonce[:])
|
w.WriteBytes(obj.Nonce[:])
|
||||||
_tmp1 := obj.BaseFee != nil
|
_tmp1 := obj.BaseFee != nil
|
||||||
_tmp2 := obj.WithdrawalsHash != nil
|
_tmp2 := obj.WithdrawalsHash != nil
|
||||||
if _tmp1 || _tmp2 {
|
_tmp3 := len(obj.TxDependency) > 0
|
||||||
|
if _tmp1 || _tmp2 || _tmp3 {
|
||||||
if obj.BaseFee == nil {
|
if obj.BaseFee == nil {
|
||||||
w.Write(rlp.EmptyString)
|
w.Write(rlp.EmptyString)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -59,6 +60,17 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
|
||||||
w.WriteBytes(obj.WithdrawalsHash[:])
|
w.WriteBytes(obj.WithdrawalsHash[:])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if _tmp3 {
|
||||||
|
_tmp3 := w.List()
|
||||||
|
for _, _tmp4 := range obj.TxDependency {
|
||||||
|
_tmp5 := w.List()
|
||||||
|
for _, _tmp6 := range _tmp4 {
|
||||||
|
w.WriteUint64(_tmp6)
|
||||||
|
}
|
||||||
|
w.ListEnd(_tmp5)
|
||||||
|
}
|
||||||
|
w.ListEnd(_tmp3)
|
||||||
|
}
|
||||||
w.ListEnd(_tmp0)
|
w.ListEnd(_tmp0)
|
||||||
return w.Flush()
|
return w.Flush()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,10 @@ type Config struct {
|
||||||
NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
|
NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
|
||||||
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
|
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
|
||||||
ExtraEips []int // Additional EIPS that are to be enabled
|
ExtraEips []int // Additional EIPS that are to be enabled
|
||||||
|
|
||||||
|
// parallel EVM configs
|
||||||
|
ParallelEnable bool
|
||||||
|
ParallelSpeculativeProcesses int
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScopeContext contains the things that are per-call, such as stack and memory,
|
// ScopeContext contains the things that are per-call, such as stack and memory,
|
||||||
|
|
@ -265,6 +269,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool, i
|
||||||
txHash, _ := GetCurrentTxFromContext(interruptCtx)
|
txHash, _ := GetCurrentTxFromContext(interruptCtx)
|
||||||
interruptedTxCache, _ := GetCache(interruptCtx)
|
interruptedTxCache, _ := GetCache(interruptCtx)
|
||||||
|
|
||||||
|
if interruptedTxCache == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
// if the tx is already in the cache, it means that it has been interrupted before and we will not interrupt it again
|
// if the tx is already in the cache, it means that it has been interrupted before and we will not interrupt it again
|
||||||
found, _ := interruptedTxCache.Cache.ContainsOrAdd(txHash, true)
|
found, _ := interruptedTxCache.Cache.ContainsOrAdd(txHash, true)
|
||||||
if found {
|
if found {
|
||||||
|
|
@ -425,6 +433,11 @@ func (in *EVMInterpreter) RunWithDelay(contract *Contract, input []byte, readOnl
|
||||||
case <-interruptCtx.Done():
|
case <-interruptCtx.Done():
|
||||||
txHash, _ := GetCurrentTxFromContext(interruptCtx)
|
txHash, _ := GetCurrentTxFromContext(interruptCtx)
|
||||||
interruptedTxCache, _ := GetCache(interruptCtx)
|
interruptedTxCache, _ := GetCache(interruptCtx)
|
||||||
|
|
||||||
|
if interruptedTxCache == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
// if the tx is already in the cache, it means that it has been interrupted before and we will not interrupt it again
|
// if the tx is already in the cache, it means that it has been interrupted before and we will not interrupt it again
|
||||||
found, _ := interruptedTxCache.Cache.ContainsOrAdd(txHash, true)
|
found, _ := interruptedTxCache.Cache.ContainsOrAdd(txHash, true)
|
||||||
log.Info("FOUND", "found", found, "txHash", txHash)
|
log.Info("FOUND", "found", found, "txHash", txHash)
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,9 @@ devfakeauthor = false # Run miner without validator set authorization
|
||||||
port = 30303 # Network listening port
|
port = 30303 # Network listening port
|
||||||
nodiscover = false # Disables the peer discovery mechanism (manual peer addition)
|
nodiscover = false # Disables the peer discovery mechanism (manual peer addition)
|
||||||
nat = "any" # NAT port mapping mechanism (any|none|upnp|pmp|extip:<IP>)
|
nat = "any" # NAT port mapping mechanism (any|none|upnp|pmp|extip:<IP>)
|
||||||
netrestrict = "" # Restricts network communication to the given IP networks (CIDR masks)
|
netrestrict = "" # Restricts network communication to the given IP networks (CIDR masks)
|
||||||
nodekey = "" # P2P node key file
|
nodekey = "" # P2P node key file
|
||||||
nodekeyhex = "" # P2P node key as hex
|
nodekeyhex = "" # P2P node key as hex
|
||||||
txarrivalwait = "500ms" # Maximum duration to wait before requesting an announced transaction
|
txarrivalwait = "500ms" # Maximum duration to wait before requesting an announced transaction
|
||||||
[p2p.discovery]
|
[p2p.discovery]
|
||||||
v5disc = false # Enables the experimental RLPx V5 (Topic Discovery) mechanism
|
v5disc = false # Enables the experimental RLPx V5 (Topic Discovery) mechanism
|
||||||
|
|
@ -73,6 +73,7 @@ devfakeauthor = false # Run miner without validator set authorization
|
||||||
gaslimit = 30000000 # Target gas ceiling for mined blocks
|
gaslimit = 30000000 # Target gas ceiling for mined blocks
|
||||||
gasprice = "1000000000" # Minimum gas price for mining a transaction (recommended for mainnet = 30000000000, default suitable for mumbai/devnet)
|
gasprice = "1000000000" # Minimum gas price for mining a transaction (recommended for mainnet = 30000000000, default suitable for mumbai/devnet)
|
||||||
recommit = "2m5s" # The time interval for miner to re-create mining work
|
recommit = "2m5s" # The time interval for miner to re-create mining work
|
||||||
|
commitinterrupt = true # Interrupt the current mining work when time is exceeded and create partial blocks
|
||||||
|
|
||||||
[jsonrpc]
|
[jsonrpc]
|
||||||
ipcdisable = false # Disable the IPC-RPC server
|
ipcdisable = false # Disable the IPC-RPC server
|
||||||
|
|
@ -129,7 +130,7 @@ devfakeauthor = false # Run miner without validator set authorization
|
||||||
metrics = false # Enable metrics collection and reporting
|
metrics = false # Enable metrics collection and reporting
|
||||||
expensive = false # Enable expensive metrics collection and reporting
|
expensive = false # Enable expensive metrics collection and reporting
|
||||||
prometheus-addr = "127.0.0.1:7071" # Address for Prometheus Server
|
prometheus-addr = "127.0.0.1:7071" # Address for Prometheus Server
|
||||||
opencollector-endpoint = "127.0.0.1:4317" # OpenCollector Endpoint (host:port)
|
opencollector-endpoint = "" # OpenCollector Endpoint (host:port)
|
||||||
[telemetry.influx]
|
[telemetry.influx]
|
||||||
influxdb = false # Enable metrics export/push to an external InfluxDB database (v1)
|
influxdb = false # Enable metrics export/push to an external InfluxDB database (v1)
|
||||||
endpoint = "" # InfluxDB API endpoint to report metrics to
|
endpoint = "" # InfluxDB API endpoint to report metrics to
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,10 @@ The ```bor server``` command runs the Bor client.
|
||||||
|
|
||||||
- ```dev.period```: Block period to use in developer mode (0 = mine only if transaction pending) (default: 0)
|
- ```dev.period```: Block period to use in developer mode (0 = mine only if transaction pending) (default: 0)
|
||||||
|
|
||||||
|
- ```parallelevm.enable```: Enable Block STM (default: true)
|
||||||
|
|
||||||
|
- ```parallelevm.procs```: Number of speculative processes (cores) in Block STM (default: 8)
|
||||||
|
|
||||||
- ```dev.gaslimit```: Initial block gas limit (default: 11500000)
|
- ```dev.gaslimit```: Initial block gas limit (default: 11500000)
|
||||||
|
|
||||||
- ```pprof```: Enable the pprof HTTP server (default: false)
|
- ```pprof```: Enable the pprof HTTP server (default: false)
|
||||||
|
|
@ -216,7 +220,7 @@ The ```bor server``` command runs the Bor client.
|
||||||
|
|
||||||
- ```v5disc```: Enables the experimental RLPx V5 (Topic Discovery) mechanism (default: false)
|
- ```v5disc```: Enables the experimental RLPx V5 (Topic Discovery) mechanism (default: false)
|
||||||
|
|
||||||
- ```txarrivalwait```: Maximum duration to wait before requesting an announced transaction (default: 500ms)
|
- ```txarrivalwait```: Maximum duration to wait for a transaction before explicitly requesting it (defaults to 500ms) (default: 500ms)
|
||||||
|
|
||||||
### Sealer Options
|
### Sealer Options
|
||||||
|
|
||||||
|
|
@ -232,6 +236,8 @@ The ```bor server``` command runs the Bor client.
|
||||||
|
|
||||||
- ```miner.recommit```: The time interval for miner to re-create mining work (default: 2m5s)
|
- ```miner.recommit```: The time interval for miner to re-create mining work (default: 2m5s)
|
||||||
|
|
||||||
|
- ```miner.interruptcommit```: Interrupt block commit when block creation time is passed (default: true)
|
||||||
|
|
||||||
### Telemetry Options
|
### Telemetry Options
|
||||||
|
|
||||||
- ```metrics```: Enable metrics collection and reporting (default: false)
|
- ```metrics```: Enable metrics collection and reporting (default: false)
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,6 @@ type Ethereum struct {
|
||||||
|
|
||||||
// New creates a new Ethereum object (including the
|
// New creates a new Ethereum object (including the
|
||||||
// initialisation of the common Ethereum object)
|
// initialisation of the common Ethereum object)
|
||||||
// TODO fixme marcello c'é qcls che non va qua (CreateConsensusEngine chiamata due volte)
|
|
||||||
func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
// Ensure configuration values are compatible and sane
|
// Ensure configuration values are compatible and sane
|
||||||
if config.SyncMode == downloader.LightSync {
|
if config.SyncMode == downloader.LightSync {
|
||||||
|
|
@ -207,7 +206,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
vmConfig = vm.Config{
|
vmConfig = vm.Config{
|
||||||
EnablePreimageRecording: config.EnablePreimageRecording,
|
EnablePreimageRecording: config.EnablePreimageRecording,
|
||||||
|
ParallelEnable: config.ParallelEVM.Enable,
|
||||||
|
ParallelSpeculativeProcesses: config.ParallelEVM.SpeculativeProcesses,
|
||||||
}
|
}
|
||||||
cacheConfig = &core.CacheConfig{
|
cacheConfig = &core.CacheConfig{
|
||||||
TrieCleanLimit: config.TrieCleanCache,
|
TrieCleanLimit: config.TrieCleanCache,
|
||||||
|
|
@ -230,7 +231,15 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
|
|
||||||
checker := whitelist.NewService(10)
|
checker := whitelist.NewService(10)
|
||||||
|
|
||||||
ethereum.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, config.Genesis, &overrides, ethereum.engine, vmConfig, ethereum.shouldPreserve, &config.TxLookupLimit, checker)
|
// check if Parallel EVM is enabled
|
||||||
|
// if enabled, use parallel state processor
|
||||||
|
if config.ParallelEVM.Enable {
|
||||||
|
// TODO marcello fix NewParallelBlockChain
|
||||||
|
ethereum.blockchain, err = core.NewParallelBlockChain(chainDb, cacheConfig, config.Genesis, ethereum.engine, vmConfig, ethereum.shouldPreserve, &config.TxLookupLimit, checker)
|
||||||
|
} else {
|
||||||
|
ethereum.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, config.Genesis, &overrides, ethereum.engine, vmConfig, ethereum.shouldPreserve, &config.TxLookupLimit, checker)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1608,9 +1608,9 @@ func TestFakedSyncProgress66NoRemoteCheckpoint(t *testing.T) {
|
||||||
|
|
||||||
tester := newTester(t)
|
tester := newTester(t)
|
||||||
validate := func(count int) (bool, error) {
|
validate := func(count int) (bool, error) {
|
||||||
// only return the `ErrNoRemoteCheckoint` error for the first call
|
// only return the `ErrNoRemoteCheckpoint` error for the first call
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
return false, whitelist.ErrNoRemoteCheckoint
|
return false, whitelist.ErrNoRemoteCheckpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
|
|
@ -1626,7 +1626,7 @@ func TestFakedSyncProgress66NoRemoteCheckpoint(t *testing.T) {
|
||||||
// Synchronise with the peer and make sure all blocks were retrieved
|
// Synchronise with the peer and make sure all blocks were retrieved
|
||||||
// Should fail in first attempt
|
// Should fail in first attempt
|
||||||
if err := tester.sync("light", nil, mode); err != nil {
|
if err := tester.sync("light", nil, mode); err != nil {
|
||||||
assert.Equal(t, whitelist.ErrNoRemoteCheckoint, err, "failed synchronisation")
|
assert.Equal(t, whitelist.ErrNoRemoteCheckpoint, err, "failed synchronisation")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try syncing again, should succeed
|
// Try syncing again, should succeed
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ func NewService(maxCapacity uint) *Service {
|
||||||
var (
|
var (
|
||||||
ErrCheckpointMismatch = errors.New("checkpoint mismatch")
|
ErrCheckpointMismatch = errors.New("checkpoint mismatch")
|
||||||
ErrLongFutureChain = errors.New("received future chain of unacceptable length")
|
ErrLongFutureChain = errors.New("received future chain of unacceptable length")
|
||||||
ErrNoRemoteCheckoint = errors.New("remote peer doesn't have a checkoint")
|
ErrNoRemoteCheckpoint = errors.New("remote peer doesn't have a checkpoint")
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsValidPeer checks if the chain we're about to receive from a peer is valid or not
|
// IsValidPeer checks if the chain we're about to receive from a peer is valid or not
|
||||||
|
|
@ -55,11 +55,11 @@ func (w *Service) IsValidPeer(remoteHeader *types.Header, fetchHeadersByNumber f
|
||||||
// todo: we can extract this as an interface and mock as well or just test IsValidChain in isolation from downloader passing fake fetchHeadersByNumber functions
|
// todo: we can extract this as an interface and mock as well or just test IsValidChain in isolation from downloader passing fake fetchHeadersByNumber functions
|
||||||
headers, hashes, err := fetchHeadersByNumber(lastCheckpointBlockNum, 1, 0, false)
|
headers, hashes, err := fetchHeadersByNumber(lastCheckpointBlockNum, 1, 0, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("%w: last checkpoint %d, err %v", ErrNoRemoteCheckoint, lastCheckpointBlockNum, err)
|
return false, fmt.Errorf("%w: last checkpoint %d, err %v", ErrNoRemoteCheckpoint, lastCheckpointBlockNum, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(headers) == 0 {
|
if len(headers) == 0 {
|
||||||
return false, fmt.Errorf("%w: last checkpoint %d", ErrNoRemoteCheckoint, lastCheckpointBlockNum)
|
return false, fmt.Errorf("%w: last checkpoint %d", ErrNoRemoteCheckpoint, lastCheckpointBlockNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqBlockNum := headers[0].Number.Uint64()
|
reqBlockNum := headers[0].Number.Uint64()
|
||||||
|
|
|
||||||
|
|
@ -64,14 +64,14 @@ func TestIsValidPeer(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// case2: false fetchHeadersByNumber function provided, should consider the chain as invalid
|
// case2: false fetchHeadersByNumber function provided, should consider the chain as invalid
|
||||||
// and throw `ErrNoRemoteCheckoint` error
|
// and throw `ErrNoRemoteCheckpoint` error
|
||||||
res, err = s.IsValidPeer(nil, falseFetchHeadersByNumber)
|
res, err = s.IsValidPeer(nil, falseFetchHeadersByNumber)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected error, got nil")
|
t.Fatal("expected error, got nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !errors.Is(err, ErrNoRemoteCheckoint) {
|
if !errors.Is(err, ErrNoRemoteCheckpoint) {
|
||||||
t.Fatalf("expected error ErrNoRemoteCheckoint, got %v", err)
|
t.Fatalf("expected error ErrNoRemoteCheckpoint, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
require.Equal(t, res, false, "expected chain to be invalid")
|
require.Equal(t, res, false, "expected chain to be invalid")
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
package ethconfig
|
package ethconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -41,6 +41,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/eth/gasprice"
|
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/miner"
|
"github.com/ethereum/go-ethereum/miner"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
|
@ -242,6 +243,9 @@ type Config struct {
|
||||||
// Bor logs flag
|
// Bor logs flag
|
||||||
BorLogs bool
|
BorLogs bool
|
||||||
|
|
||||||
|
// Parallel EVM (Block-STM) related config
|
||||||
|
ParallelEVM core.ParallelEVMConfig `toml:",omitempty"`
|
||||||
|
|
||||||
// Develop Fake Author mode to produce blocks without authorisation
|
// Develop Fake Author mode to produce blocks without authorisation
|
||||||
DevFakeAuthor bool `hcl:"devfakeauthor,optional" toml:"devfakeauthor,optional"`
|
DevFakeAuthor bool `hcl:"devfakeauthor,optional" toml:"devfakeauthor,optional"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,7 @@ func (p *Peer) dispatcher() {
|
||||||
}
|
}
|
||||||
// Stop tracking the request
|
// Stop tracking the request
|
||||||
delete(pending, cancelOp.id)
|
delete(pending, cancelOp.id)
|
||||||
|
requestTracker.Fulfil(p.id, p.version, req.code, cancelOp.id)
|
||||||
cancelOp.fail <- nil
|
cancelOp.fail <- nil
|
||||||
|
|
||||||
case resOp := <-p.resDispatch:
|
case resOp := <-p.resDispatch:
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,9 @@ func (eth *Ethereum) StateAtBlock(ctx context.Context, block *types.Block, reexe
|
||||||
if current = eth.blockchain.GetBlockByNumber(next); current == nil {
|
if current = eth.blockchain.GetBlockByNumber(next); current == nil {
|
||||||
return nil, nil, fmt.Errorf("block #%d not found", next)
|
return nil, nil, fmt.Errorf("block #%d not found", next)
|
||||||
}
|
}
|
||||||
_, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{})
|
|
||||||
|
_, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{}, nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err)
|
return nil, nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,14 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -68,6 +72,10 @@ const (
|
||||||
// for tracing. The creation of trace state will be paused if the unused
|
// for tracing. The creation of trace state will be paused if the unused
|
||||||
// trace states exceed this limit.
|
// trace states exceed this limit.
|
||||||
maximumPendingTraceStates = 128
|
maximumPendingTraceStates = 128
|
||||||
|
|
||||||
|
defaultPath = string(".")
|
||||||
|
|
||||||
|
defaultIOFlag = false
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultBorTraceEnabled = newBoolPtr(false)
|
var defaultBorTraceEnabled = newBoolPtr(false)
|
||||||
|
|
@ -78,6 +86,8 @@ var errTxNotFound = errors.New("transaction not found")
|
||||||
// historical state for tracing purposes.
|
// historical state for tracing purposes.
|
||||||
type StateReleaseFunc func()
|
type StateReleaseFunc func()
|
||||||
|
|
||||||
|
var allowIOTracing = false // Change this to true to enable IO tracing for debugging
|
||||||
|
|
||||||
// Backend interface provides the common API services (that are provided by
|
// Backend interface provides the common API services (that are provided by
|
||||||
// both full and light clients) with access to necessary functions.
|
// both full and light clients) with access to necessary functions.
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
|
|
@ -204,9 +214,11 @@ type TraceConfig struct {
|
||||||
Tracer *string
|
Tracer *string
|
||||||
Timeout *string
|
Timeout *string
|
||||||
Reexec *uint64
|
Reexec *uint64
|
||||||
|
Path *string
|
||||||
|
IOFlag *bool
|
||||||
// Config specific to given tracer. Note struct logger
|
// Config specific to given tracer. Note struct logger
|
||||||
// config are historically embedded in main object.
|
// config are historically embedded in main object.
|
||||||
TracerConfig json.RawMessage
|
TracerConfig json.RawMessage
|
||||||
BorTraceEnabled *bool
|
BorTraceEnabled *bool
|
||||||
BorTx *bool
|
BorTx *bool
|
||||||
}
|
}
|
||||||
|
|
@ -222,8 +234,8 @@ type TraceCallConfig struct {
|
||||||
// StdTraceConfig holds extra parameters to standard-json trace functions.
|
// StdTraceConfig holds extra parameters to standard-json trace functions.
|
||||||
type StdTraceConfig struct {
|
type StdTraceConfig struct {
|
||||||
logger.Config
|
logger.Config
|
||||||
Reexec *uint64
|
Reexec *uint64
|
||||||
TxHash common.Hash
|
TxHash common.Hash
|
||||||
BorTraceEnabled *bool
|
BorTraceEnabled *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -691,7 +703,6 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
|
||||||
return roots, nil
|
return roots, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// StandardTraceBadBlockToFile dumps the structured logs created during the
|
// StandardTraceBadBlockToFile dumps the structured logs created during the
|
||||||
// execution of EVM against a block pulled from the pool of bad ones to the
|
// execution of EVM against a block pulled from the pool of bad ones to the
|
||||||
// local file system and returns a list of files to the caller.
|
// local file system and returns a list of files to the caller.
|
||||||
|
|
@ -734,19 +745,36 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
|
||||||
if config != nil && config.Reexec != nil {
|
if config != nil && config.Reexec != nil {
|
||||||
reexec = *config.Reexec
|
reexec = *config.Reexec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path := defaultPath
|
||||||
|
if config != nil && config.Path != nil {
|
||||||
|
path = *config.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
ioflag := defaultIOFlag
|
||||||
|
if allowIOTracing && config != nil && config.IOFlag != nil {
|
||||||
|
ioflag = *config.IOFlag
|
||||||
|
}
|
||||||
|
|
||||||
statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
|
statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer release()
|
defer release()
|
||||||
|
|
||||||
|
// create and add empty mvHashMap in statedb as StateAtBlock does not have mvHashmap in it.
|
||||||
|
if ioflag {
|
||||||
|
statedb.AddEmptyMVHashMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute all the transaction contained within the block concurrently
|
||||||
var (
|
var (
|
||||||
txs, stateSyncPresent = api.getAllBlockTransactions(ctx, block)
|
txs, stateSyncPresent = api.getAllBlockTransactions(ctx, block)
|
||||||
blockHash = block.Hash()
|
blockHash = block.Hash()
|
||||||
blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||||
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
|
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
|
||||||
results = make([]*txTraceResult, len(txs))
|
results = make([]*txTraceResult, len(txs))
|
||||||
pend sync.WaitGroup
|
pend sync.WaitGroup
|
||||||
)
|
)
|
||||||
threads := runtime.NumCPU()
|
threads := runtime.NumCPU()
|
||||||
if threads > len(txs) {
|
if threads > len(txs) {
|
||||||
|
|
@ -790,10 +818,30 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var IOdump string
|
||||||
|
|
||||||
|
var RWstruct []state.DumpStruct
|
||||||
|
|
||||||
|
var london bool
|
||||||
|
|
||||||
|
if ioflag {
|
||||||
|
IOdump = "TransactionIndex, Incarnation, VersionTxIdx, VersionInc, Path, Operation\n"
|
||||||
|
RWstruct = []state.DumpStruct{}
|
||||||
|
}
|
||||||
// Feed the transactions into the tracers and return
|
// Feed the transactions into the tracers and return
|
||||||
var failed error
|
var failed error
|
||||||
|
|
||||||
|
if ioflag {
|
||||||
|
london = api.backend.ChainConfig().IsLondon(block.Number())
|
||||||
|
}
|
||||||
|
|
||||||
txloop:
|
txloop:
|
||||||
for i, tx := range txs {
|
for i, tx := range txs {
|
||||||
|
if ioflag {
|
||||||
|
// copy of statedb
|
||||||
|
statedb = statedb.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
// Send the trace task over for execution
|
// Send the trace task over for execution
|
||||||
task := &txTraceTask{statedb: statedb.Copy(), index: i}
|
task := &txTraceTask{statedb: statedb.Copy(), index: i}
|
||||||
select {
|
select {
|
||||||
|
|
@ -807,27 +855,88 @@ txloop:
|
||||||
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
|
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
statedb.SetTxContext(tx.Hash(), i)
|
||||||
vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{})
|
vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{})
|
||||||
//nolint: nestif
|
|
||||||
if stateSyncPresent && i == len(txs)-1 {
|
// nolint: nestif
|
||||||
if *config.BorTraceEnabled {
|
if !ioflag {
|
||||||
callmsg := prepareCallMessage(*msg)
|
//nolint: nestif
|
||||||
if _, err := statefull.ApplyBorMessage(*vmenv, callmsg); err != nil {
|
if stateSyncPresent && i == len(txs)-1 {
|
||||||
failed = err
|
if *config.BorTraceEnabled {
|
||||||
|
callmsg := prepareCallMessage(*msg)
|
||||||
|
// nolint : contextcheck
|
||||||
|
if _, err := statefull.ApplyBorMessage(*vmenv, callmsg); err != nil {
|
||||||
|
failed = err
|
||||||
|
break txloop
|
||||||
|
}
|
||||||
|
} else {
|
||||||
break txloop
|
break txloop
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
break txloop
|
// nolint : contextcheck
|
||||||
|
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit), context.Background()); err != nil {
|
||||||
|
failed = err
|
||||||
|
break txloop
|
||||||
|
}
|
||||||
|
// Finalize the state so any modifications are written to the trie
|
||||||
|
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
||||||
|
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
coinbaseBalance := statedb.GetBalance(blockCtx.Coinbase)
|
||||||
// nolint : contextcheck
|
// nolint : contextcheck
|
||||||
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit), context.Background()); err != nil {
|
result, err := core.ApplyMessageNoFeeBurnOrTip(vmenv, *msg, new(core.GasPool).AddGas(msg.GasLimit), context.Background())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
failed = err
|
failed = err
|
||||||
break txloop
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if london {
|
||||||
|
statedb.AddBalance(result.BurntContractAddress, result.FeeBurnt)
|
||||||
|
}
|
||||||
|
|
||||||
|
statedb.AddBalance(blockCtx.Coinbase, result.FeeTipped)
|
||||||
|
output1 := new(big.Int).SetBytes(result.SenderInitBalance.Bytes())
|
||||||
|
output2 := new(big.Int).SetBytes(coinbaseBalance.Bytes())
|
||||||
|
|
||||||
|
// Deprecating transfer log and will be removed in future fork. PLEASE DO NOT USE this transfer log going forward. Parameters won't get updated as expected going forward with EIP1559
|
||||||
|
// add transfer log
|
||||||
|
core.AddFeeTransferLog(
|
||||||
|
statedb,
|
||||||
|
|
||||||
|
msg.From,
|
||||||
|
blockCtx.Coinbase,
|
||||||
|
|
||||||
|
result.FeeTipped,
|
||||||
|
result.SenderInitBalance,
|
||||||
|
coinbaseBalance,
|
||||||
|
output1.Sub(output1, result.FeeTipped),
|
||||||
|
output2.Add(output2, result.FeeTipped),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Finalize the state so any modifications are written to the trie
|
||||||
|
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
||||||
|
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
|
||||||
|
statedb.FlushMVWriteSet()
|
||||||
|
|
||||||
|
structRead := statedb.GetReadMapDump()
|
||||||
|
structWrite := statedb.GetWriteMapDump()
|
||||||
|
|
||||||
|
RWstruct = append(RWstruct, structRead...)
|
||||||
|
RWstruct = append(RWstruct, structWrite...)
|
||||||
}
|
}
|
||||||
// Finalize the state so any modifications are written to the trie
|
}
|
||||||
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
|
||||||
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
|
if ioflag {
|
||||||
|
for _, val := range RWstruct {
|
||||||
|
IOdump += fmt.Sprintf("%v , %v, %v , %v, ", val.TxIdx, val.TxInc, val.VerIdx, val.VerInc) + hex.EncodeToString(val.Path) + ", " + val.Op
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that the file exists and write IOdump
|
||||||
|
err = ioutil.WriteFile(filepath.Join(path, "data.csv"), []byte(fmt.Sprint(IOdump)), 0600)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close(jobs)
|
close(jobs)
|
||||||
|
|
@ -845,7 +954,6 @@ txloop:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// standardTraceBlockToFile configures a new tracer which uses standard JSON output,
|
// standardTraceBlockToFile configures a new tracer which uses standard JSON output,
|
||||||
// and traces either a full block or an individual transaction. The return value will
|
// and traces either a full block or an individual transaction. The return value will
|
||||||
// be one filename per transaction traced.
|
// be one filename per transaction traced.
|
||||||
|
|
@ -1177,7 +1285,7 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *Conte
|
||||||
} else {
|
} else {
|
||||||
// nolint : contextcheck
|
// nolint : contextcheck
|
||||||
if _, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.GasLimit), context.Background()); err != nil {
|
if _, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.GasLimit), context.Background()); err != nil {
|
||||||
return nil, fmt.Errorf("tracing failed: %w", err)
|
return nil, fmt.Errorf("tracing failed: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tracer.GetResult()
|
return tracer.GetResult()
|
||||||
|
|
|
||||||
|
|
@ -460,6 +460,85 @@ func TestTraceBlock(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIOdump(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Initialize test accounts
|
||||||
|
accounts := newAccounts(5)
|
||||||
|
genesis := &core.Genesis{Alloc: core.GenesisAlloc{
|
||||||
|
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[3].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[4].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
}}
|
||||||
|
genBlocks := 1
|
||||||
|
signer := types.HomesteadSigner{}
|
||||||
|
api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
|
||||||
|
// Transfer from account[0] to account[1], account[1] to account[2], account[2] to account[3], account[3] to account[4], account[4] to account[0]
|
||||||
|
// value: 1000 wei
|
||||||
|
// fee: 0 wei
|
||||||
|
|
||||||
|
for j := 0; j < 5; j++ {
|
||||||
|
tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[(j+1)%5].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[j].key)
|
||||||
|
b.AddTx(tx)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
allowIOTracing = true
|
||||||
|
|
||||||
|
ioflag := new(bool)
|
||||||
|
|
||||||
|
*ioflag = true
|
||||||
|
|
||||||
|
var testSuite = []struct {
|
||||||
|
blockNumber rpc.BlockNumber
|
||||||
|
config *TraceConfig
|
||||||
|
want string
|
||||||
|
expectErr error
|
||||||
|
}{
|
||||||
|
// Trace head block
|
||||||
|
{
|
||||||
|
config: &TraceConfig{
|
||||||
|
IOFlag: ioflag,
|
||||||
|
},
|
||||||
|
blockNumber: rpc.BlockNumber(genBlocks),
|
||||||
|
want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}},{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}},{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}},{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}},{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range testSuite {
|
||||||
|
result, err := api.TraceBlockByNumber(context.Background(), tc.blockNumber, tc.config)
|
||||||
|
if tc.expectErr != nil {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("test %d, want error %v", i, tc.expectErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(err, tc.expectErr) {
|
||||||
|
t.Errorf("test %d: error mismatch, want %v, get %v", i, tc.expectErr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test %d, want no error, have %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
have, err := json.Marshal(result)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error in Marshal: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := tc.want
|
||||||
|
if string(have) != want {
|
||||||
|
t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, string(have), want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTracingWithOverrides(t *testing.T) {
|
func TestTracingWithOverrides(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
// Initialize test accounts
|
// Initialize test accounts
|
||||||
|
|
|
||||||
6
go.mod
6
go.mod
|
|
@ -43,6 +43,8 @@ require (
|
||||||
github.com/hashicorp/go-bexpr v0.1.10
|
github.com/hashicorp/go-bexpr v0.1.10
|
||||||
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
|
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
|
||||||
github.com/hashicorp/hcl/v2 v2.10.1
|
github.com/hashicorp/hcl/v2 v2.10.1
|
||||||
|
github.com/heimdalr/dag v1.2.1
|
||||||
|
github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e
|
||||||
github.com/holiman/bloomfilter/v2 v2.0.3
|
github.com/holiman/bloomfilter/v2 v2.0.3
|
||||||
github.com/holiman/uint256 v1.2.2-0.20230321075855-87b91420868c
|
github.com/holiman/uint256 v1.2.2-0.20230321075855-87b91420868c
|
||||||
github.com/huin/goupnp v1.0.3
|
github.com/huin/goupnp v1.0.3
|
||||||
|
|
@ -180,6 +182,10 @@ require (
|
||||||
github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8 // indirect
|
github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8 // indirect
|
||||||
github.com/cosmos/ledger-cosmos-go v0.10.3 // indirect
|
github.com/cosmos/ledger-cosmos-go v0.10.3 // indirect
|
||||||
github.com/cosmos/ledger-go v0.9.2 // indirect
|
github.com/cosmos/ledger-go v0.9.2 // indirect
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
||||||
|
github.com/deepmap/oapi-codegen v1.8.2 // indirect
|
||||||
|
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // indirect
|
||||||
|
github.com/emirpasic/gods v1.18.1
|
||||||
github.com/etcd-io/bbolt v1.3.3 // indirect
|
github.com/etcd-io/bbolt v1.3.3 // indirect
|
||||||
github.com/go-kit/kit v0.10.0 // indirect
|
github.com/go-kit/kit v0.10.0 // indirect
|
||||||
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
||||||
|
|
|
||||||
|
|
@ -1067,7 +1067,8 @@ func (c *CallResult) Status() Long {
|
||||||
func (b *Block) Call(ctx context.Context, args struct {
|
func (b *Block) Call(ctx context.Context, args struct {
|
||||||
Data ethapi.TransactionArgs
|
Data ethapi.TransactionArgs
|
||||||
}) (*CallResult, error) {
|
}) (*CallResult, error) {
|
||||||
result, err := ethapi.DoCall(ctx, b.r.backend, args.Data, *b.numberOrHash, nil, b.r.backend.RPCEVMTimeout(), b.r.backend.RPCGasCap())
|
// TODO marcello check args number
|
||||||
|
result, err := ethapi.DoCall(ctx, b.r.backend, args.Data, *b.numberOrHash, nil, nil, b.r.backend.RPCEVMTimeout(), b.r.backend.RPCGasCap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1131,7 +1132,7 @@ func (p *Pending) Call(ctx context.Context, args struct {
|
||||||
Data ethapi.TransactionArgs
|
Data ethapi.TransactionArgs
|
||||||
}) (*CallResult, error) {
|
}) (*CallResult, error) {
|
||||||
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
||||||
result, err := ethapi.DoCall(ctx, p.r.backend, args.Data, pendingBlockNr, nil, p.r.backend.RPCEVMTimeout(), p.r.backend.RPCGasCap())
|
result, err := ethapi.DoCall(ctx, p.r.backend, args.Data, pendingBlockNr, nil, nil, p.r.backend.RPCEVMTimeout(), p.r.backend.RPCGasCap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,13 @@ var mumbaiTestnet = &Chain{
|
||||||
BerlinBlock: big.NewInt(13996000),
|
BerlinBlock: big.NewInt(13996000),
|
||||||
LondonBlock: big.NewInt(22640000),
|
LondonBlock: big.NewInt(22640000),
|
||||||
Bor: ¶ms.BorConfig{
|
Bor: ¶ms.BorConfig{
|
||||||
JaipurBlock: big.NewInt(22770000),
|
JaipurBlock: big.NewInt(22770000),
|
||||||
DelhiBlock: big.NewInt(29638656),
|
DelhiBlock: big.NewInt(29638656),
|
||||||
|
ParallelUniverseBlock: big.NewInt(0),
|
||||||
|
IndoreBlock: big.NewInt(36877056),
|
||||||
|
StateSyncConfirmationDelay: map[string]uint64{
|
||||||
|
"36877056": 128,
|
||||||
|
},
|
||||||
Period: map[string]uint64{
|
Period: map[string]uint64{
|
||||||
"0": 2,
|
"0": 2,
|
||||||
"25275000": 5,
|
"25275000": 5,
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,12 @@
|
||||||
"22640000": "0x70bcA57F4579f58670aB2d18Ef16e02C17553C38"
|
"22640000": "0x70bcA57F4579f58670aB2d18Ef16e02C17553C38"
|
||||||
},
|
},
|
||||||
"jaipurBlock": 22770000,
|
"jaipurBlock": 22770000,
|
||||||
"delhiBlock": 29638656
|
"delhiBlock": 29638656,
|
||||||
|
"parallelUniverseBlock": 0,
|
||||||
|
"indoreBlock": 36877056,
|
||||||
|
"stateSyncConfirmationDelay": {
|
||||||
|
"36877056": 128
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nonce": "0x0",
|
"nonce": "0x0",
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,12 @@
|
||||||
"22640000":"0x70bcA57F4579f58670aB2d18Ef16e02C17553C38"
|
"22640000":"0x70bcA57F4579f58670aB2d18Ef16e02C17553C38"
|
||||||
},
|
},
|
||||||
"jaipurBlock":22770000,
|
"jaipurBlock":22770000,
|
||||||
"delhiBlock": 29638656
|
"delhiBlock": 29638656,
|
||||||
|
"parallelUniverseBlock": 0,
|
||||||
|
"indoreBlock": 36877056,
|
||||||
|
"stateSyncConfirmationDelay": {
|
||||||
|
"36877056": 128
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nonce":"0x0",
|
"nonce":"0x0",
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,9 @@ type Config struct {
|
||||||
// Developer has the developer mode related settings
|
// Developer has the developer mode related settings
|
||||||
Developer *DeveloperConfig `hcl:"developer,block" toml:"developer,block"`
|
Developer *DeveloperConfig `hcl:"developer,block" toml:"developer,block"`
|
||||||
|
|
||||||
|
// ParallelEVM has the parallel evm related settings
|
||||||
|
ParallelEVM *ParallelEVMConfig `hcl:"parallelevm,block" toml:"parallelevm,block"`
|
||||||
|
|
||||||
// Develop Fake Author mode to produce blocks without authorisation
|
// Develop Fake Author mode to produce blocks without authorisation
|
||||||
DevFakeAuthor bool `hcl:"devfakeauthor,optional" toml:"devfakeauthor,optional"`
|
DevFakeAuthor bool `hcl:"devfakeauthor,optional" toml:"devfakeauthor,optional"`
|
||||||
|
|
||||||
|
|
@ -309,9 +312,10 @@ type SealerConfig struct {
|
||||||
GasPriceRaw string `hcl:"gasprice,optional" toml:"gasprice,optional"`
|
GasPriceRaw string `hcl:"gasprice,optional" toml:"gasprice,optional"`
|
||||||
|
|
||||||
// The time interval for miner to re-create mining work.
|
// The time interval for miner to re-create mining work.
|
||||||
Recommit time.Duration `hcl:"-,optional" toml:"-"`
|
Recommit time.Duration `hcl:"-,optional" toml:"-"`
|
||||||
RecommitRaw string `hcl:"recommit,optional" toml:"recommit,optional"`
|
RecommitRaw string `hcl:"recommit,optional" toml:"recommit,optional"`
|
||||||
CommitInterruptFlag bool `hcl:"commitinterrupt,optional" toml:"commitinterrupt,optional"`
|
|
||||||
|
CommitInterruptFlag bool `hcl:"commitinterrupt,optional" toml:"commitinterrupt,optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type JsonRPCConfig struct {
|
type JsonRPCConfig struct {
|
||||||
|
|
@ -569,6 +573,12 @@ type DeveloperConfig struct {
|
||||||
GasLimit uint64 `hcl:"gaslimit,optional" toml:"gaslimit,optional"`
|
GasLimit uint64 `hcl:"gaslimit,optional" toml:"gaslimit,optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ParallelEVMConfig struct {
|
||||||
|
Enable bool `hcl:"enable,optional" toml:"enable,optional"`
|
||||||
|
|
||||||
|
SpeculativeProcesses int `hcl:"procs,optional" toml:"procs,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
func DefaultConfig() *Config {
|
func DefaultConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
Chain: "mainnet",
|
Chain: "mainnet",
|
||||||
|
|
@ -695,7 +705,7 @@ func DefaultConfig() *Config {
|
||||||
Enabled: false,
|
Enabled: false,
|
||||||
Expensive: false,
|
Expensive: false,
|
||||||
PrometheusAddr: "127.0.0.1:7071",
|
PrometheusAddr: "127.0.0.1:7071",
|
||||||
OpenCollectorEndpoint: "127.0.0.1:4317",
|
OpenCollectorEndpoint: "",
|
||||||
InfluxDB: &InfluxDBConfig{
|
InfluxDB: &InfluxDBConfig{
|
||||||
V1Enabled: false,
|
V1Enabled: false,
|
||||||
Endpoint: "",
|
Endpoint: "",
|
||||||
|
|
@ -748,6 +758,10 @@ func DefaultConfig() *Config {
|
||||||
BlockProfileRate: 0,
|
BlockProfileRate: 0,
|
||||||
// CPUProfile: "",
|
// CPUProfile: "",
|
||||||
},
|
},
|
||||||
|
ParallelEVM: &ParallelEVMConfig{
|
||||||
|
Enable: true,
|
||||||
|
SpeculativeProcesses: 8,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1131,6 +1145,8 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
|
||||||
n.BorLogs = c.BorLogs
|
n.BorLogs = c.BorLogs
|
||||||
n.DatabaseHandles = dbHandles
|
n.DatabaseHandles = dbHandles
|
||||||
|
|
||||||
|
n.ParallelEVM.Enable = c.ParallelEVM.Enable
|
||||||
|
n.ParallelEVM.SpeculativeProcesses = c.ParallelEVM.SpeculativeProcesses
|
||||||
n.RPCReturnDataLimit = c.RPCReturnDataLimit
|
n.RPCReturnDataLimit = c.RPCReturnDataLimit
|
||||||
|
|
||||||
if c.Ancient != "" {
|
if c.Ancient != "" {
|
||||||
|
|
|
||||||
|
|
@ -904,6 +904,20 @@ func (c *Command) Flags() *flagset.Flagset {
|
||||||
Value: &c.cliConfig.Developer.Period,
|
Value: &c.cliConfig.Developer.Period,
|
||||||
Default: c.cliConfig.Developer.Period,
|
Default: c.cliConfig.Developer.Period,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// parallelevm
|
||||||
|
f.BoolFlag(&flagset.BoolFlag{
|
||||||
|
Name: "parallelevm.enable",
|
||||||
|
Usage: "Enable Block STM",
|
||||||
|
Value: &c.cliConfig.ParallelEVM.Enable,
|
||||||
|
Default: c.cliConfig.ParallelEVM.Enable,
|
||||||
|
})
|
||||||
|
f.IntFlag(&flagset.IntFlag{
|
||||||
|
Name: "parallelevm.procs",
|
||||||
|
Usage: "Number of speculative processes (cores) in Block STM",
|
||||||
|
Value: &c.cliConfig.ParallelEVM.SpeculativeProcesses,
|
||||||
|
Default: c.cliConfig.ParallelEVM.SpeculativeProcesses,
|
||||||
|
})
|
||||||
f.Uint64Flag(&flagset.Uint64Flag{
|
f.Uint64Flag(&flagset.Uint64Flag{
|
||||||
Name: "dev.gaslimit",
|
Name: "dev.gaslimit",
|
||||||
Usage: "Initial block gas limit",
|
Usage: "Initial block gas limit",
|
||||||
|
|
|
||||||
|
|
@ -1053,16 +1053,40 @@ func (diff *BlockOverrides) Apply(blockCtx *vm.BlockContext) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
|
func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, state *state.StateDB, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
|
||||||
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
||||||
|
|
||||||
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
var (
|
||||||
if state == nil || err != nil {
|
header *types.Header
|
||||||
return nil, err
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fetch the state and header from blockNumberOrHash if it's coming from normal eth_call path.
|
||||||
|
if state == nil {
|
||||||
|
state, header, err = b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||||
|
if state == nil || err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fetch the header from the given blockNumberOrHash. Note that this path is only taken
|
||||||
|
// when we're doing a call from bor consensus to fetch data from genesis contracts. It's
|
||||||
|
// necessary to fetch header using header hash as we might be experiencing a reorg and there
|
||||||
|
// can be multiple headers with same number.
|
||||||
|
header, err = b.HeaderByHash(ctx, *blockNrOrHash.BlockHash)
|
||||||
|
if header == nil || err != nil {
|
||||||
|
log.Warn("Error fetching header on CallWithState", "err", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := overrides.Apply(state); err != nil {
|
if err := overrides.Apply(state); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return doCallWithState(ctx, b, args, header, state, timeout, globalGasCap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doCallWithState(ctx context.Context, b Backend, args TransactionArgs, header *types.Header, state *state.StateDB, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
|
||||||
// Setup context so it may be cancelled the call has completed
|
// Setup context so it may be cancelled the call has completed
|
||||||
// or, in case of unmetered gas, setup a context with a timeout.
|
// or, in case of unmetered gas, setup a context with a timeout.
|
||||||
var cancel context.CancelFunc
|
var cancel context.CancelFunc
|
||||||
|
|
@ -1146,7 +1170,20 @@ func (e *revertError) ErrorData() interface{} {
|
||||||
// Note, this function doesn't make and changes in the state/blockchain and is
|
// Note, this function doesn't make and changes in the state/blockchain and is
|
||||||
// useful to execute and retrieve values.
|
// useful to execute and retrieve values.
|
||||||
func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Bytes, error) {
|
func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Bytes, error) {
|
||||||
result, err := DoCall(ctx, s.b, args, blockNrOrHash, overrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap())
|
return s.CallWithState(ctx, args, blockNrOrHash, nil, overrides)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallWithState executes the given transaction on the given state for
|
||||||
|
// the given block number. Note that as it does an EVM call, fields in
|
||||||
|
// the underlying state will change. Make sure to handle it outside of
|
||||||
|
// this function (ideally by sending a copy of state).
|
||||||
|
//
|
||||||
|
// Additionally, the caller can specify a batch of contract for fields overriding.
|
||||||
|
//
|
||||||
|
// Note, this function doesn't make and changes in the state/blockchain and is
|
||||||
|
// useful to execute and retrieve values.
|
||||||
|
func (s *BlockChainAPI) CallWithState(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, state *state.StateDB, overrides *StateOverride) (hexutil.Bytes, error) {
|
||||||
|
result, err := DoCall(ctx, s.b, args, blockNrOrHash, state, overrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1236,7 +1273,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
|
||||||
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
|
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
|
||||||
args.Gas = (*hexutil.Uint64)(&gas)
|
args.Gas = (*hexutil.Uint64)(&gas)
|
||||||
|
|
||||||
result, err := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap)
|
result, err := DoCall(ctx, b, args, blockNrOrHash, nil, nil, 0, gasCap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, core.ErrIntrinsicGas) {
|
if errors.Is(err, core.ErrIntrinsicGas) {
|
||||||
return true, nil, nil // Special case, raise gas limit
|
return true, nil, nil // Special case, raise gas limit
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,13 @@ const skipLevel = 2
|
||||||
type Lvl int
|
type Lvl int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LvlDiscard Lvl = -1
|
LvlCrit Lvl = iota
|
||||||
LvlCrit Lvl = iota
|
|
||||||
LvlError
|
LvlError
|
||||||
LvlWarn
|
LvlWarn
|
||||||
LvlInfo
|
LvlInfo
|
||||||
LvlDebug
|
LvlDebug
|
||||||
LvlTrace
|
LvlTrace
|
||||||
|
LvlDiscard Lvl = -1
|
||||||
)
|
)
|
||||||
|
|
||||||
// AlignedString returns a 5-character string containing the name of a Lvl.
|
// AlignedString returns a 5-character string containing the name of a Lvl.
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ var (
|
||||||
typeCounterTpl = "# TYPE %s counter\n"
|
typeCounterTpl = "# TYPE %s counter\n"
|
||||||
typeSummaryTpl = "# TYPE %s summary\n"
|
typeSummaryTpl = "# TYPE %s summary\n"
|
||||||
keyValueTpl = "%s %v\n\n"
|
keyValueTpl = "%s %v\n\n"
|
||||||
|
keyCounterTpl = "%s %v\n"
|
||||||
keyQuantileTagValueTpl = "%s {quantile=\"%s\"} %v\n"
|
keyQuantileTagValueTpl = "%s {quantile=\"%s\"} %v\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -65,11 +66,16 @@ func (c *collector) addGaugeFloat64(name string, m metrics.GaugeFloat64) {
|
||||||
func (c *collector) addHistogram(name string, m metrics.Histogram) {
|
func (c *collector) addHistogram(name string, m metrics.Histogram) {
|
||||||
pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}
|
pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}
|
||||||
ps := m.Percentiles(pv)
|
ps := m.Percentiles(pv)
|
||||||
c.writeSummaryCounter(name, m.Count())
|
|
||||||
|
var sum float64 = 0
|
||||||
c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, mutateKey(name)))
|
c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, mutateKey(name)))
|
||||||
for i := range pv {
|
for i := range pv {
|
||||||
c.writeSummaryPercentile(name, strconv.FormatFloat(pv[i], 'f', -1, 64), ps[i])
|
c.writeSummaryPercentile(name, strconv.FormatFloat(pv[i], 'f', -1, 64), ps[i])
|
||||||
|
sum += ps[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.writeSummarySum(name, fmt.Sprintf("%f", sum))
|
||||||
|
c.writeSummaryCounter(name, len(ps))
|
||||||
c.buff.WriteRune('\n')
|
c.buff.WriteRune('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,7 +86,7 @@ func (c *collector) addMeter(name string, m metrics.Meter) {
|
||||||
func (c *collector) addTimer(name string, m metrics.Timer) {
|
func (c *collector) addTimer(name string, m metrics.Timer) {
|
||||||
pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}
|
pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}
|
||||||
ps := m.Percentiles(pv)
|
ps := m.Percentiles(pv)
|
||||||
c.writeSummaryCounter(name, m.Count())
|
c.writeCounter(name, m.Count())
|
||||||
c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, mutateKey(name)))
|
c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, mutateKey(name)))
|
||||||
for i := range pv {
|
for i := range pv {
|
||||||
c.writeSummaryPercentile(name, strconv.FormatFloat(pv[i], 'f', -1, 64), ps[i])
|
c.writeSummaryPercentile(name, strconv.FormatFloat(pv[i], 'f', -1, 64), ps[i])
|
||||||
|
|
@ -94,11 +100,19 @@ func (c *collector) addResettingTimer(name string, m metrics.ResettingTimer) {
|
||||||
}
|
}
|
||||||
ps := m.Percentiles([]float64{50, 95, 99})
|
ps := m.Percentiles([]float64{50, 95, 99})
|
||||||
val := m.Values()
|
val := m.Values()
|
||||||
c.writeSummaryCounter(name, len(val))
|
|
||||||
c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, mutateKey(name)))
|
c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, mutateKey(name)))
|
||||||
c.writeSummaryPercentile(name, "0.50", ps[0])
|
c.writeSummaryPercentile(name, "0.50", ps[0])
|
||||||
c.writeSummaryPercentile(name, "0.95", ps[1])
|
c.writeSummaryPercentile(name, "0.95", ps[1])
|
||||||
c.writeSummaryPercentile(name, "0.99", ps[2])
|
c.writeSummaryPercentile(name, "0.99", ps[2])
|
||||||
|
|
||||||
|
var sum int64 = 0
|
||||||
|
|
||||||
|
for _, v := range val {
|
||||||
|
sum += v
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeSummarySum(name, fmt.Sprintf("%d", sum))
|
||||||
|
c.writeSummaryCounter(name, len(val))
|
||||||
c.buff.WriteRune('\n')
|
c.buff.WriteRune('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,17 +122,27 @@ func (c *collector) writeGaugeCounter(name string, value interface{}) {
|
||||||
c.buff.WriteString(fmt.Sprintf(keyValueTpl, name, value))
|
c.buff.WriteString(fmt.Sprintf(keyValueTpl, name, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *collector) writeSummaryCounter(name string, value interface{}) {
|
func (c *collector) writeCounter(name string, value interface{}) {
|
||||||
name = mutateKey(name + "_count")
|
name = mutateKey(name + "_count")
|
||||||
c.buff.WriteString(fmt.Sprintf(typeCounterTpl, name))
|
c.buff.WriteString(fmt.Sprintf(typeCounterTpl, name))
|
||||||
c.buff.WriteString(fmt.Sprintf(keyValueTpl, name, value))
|
c.buff.WriteString(fmt.Sprintf(keyValueTpl, name, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *collector) writeSummaryCounter(name string, value interface{}) {
|
||||||
|
name = mutateKey(name + "_count")
|
||||||
|
c.buff.WriteString(fmt.Sprintf(keyCounterTpl, name, value))
|
||||||
|
}
|
||||||
|
|
||||||
func (c *collector) writeSummaryPercentile(name, p string, value interface{}) {
|
func (c *collector) writeSummaryPercentile(name, p string, value interface{}) {
|
||||||
name = mutateKey(name)
|
name = mutateKey(name)
|
||||||
c.buff.WriteString(fmt.Sprintf(keyQuantileTagValueTpl, name, p, value))
|
c.buff.WriteString(fmt.Sprintf(keyQuantileTagValueTpl, name, p, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *collector) writeSummarySum(name string, value string) {
|
||||||
|
name = mutateKey(name + "_sum")
|
||||||
|
c.buff.WriteString(fmt.Sprintf(keyCounterTpl, name, value))
|
||||||
|
}
|
||||||
|
|
||||||
func mutateKey(key string) string {
|
func mutateKey(key string) string {
|
||||||
return strings.ReplaceAll(key, "/", "_")
|
return strings.ReplaceAll(key, "/", "_")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,9 +74,6 @@ test_gauge 23456
|
||||||
# TYPE test_gauge_float64 gauge
|
# TYPE test_gauge_float64 gauge
|
||||||
test_gauge_float64 34567.89
|
test_gauge_float64 34567.89
|
||||||
|
|
||||||
# TYPE test_histogram_count counter
|
|
||||||
test_histogram_count 0
|
|
||||||
|
|
||||||
# TYPE test_histogram summary
|
# TYPE test_histogram summary
|
||||||
test_histogram {quantile="0.5"} 0
|
test_histogram {quantile="0.5"} 0
|
||||||
test_histogram {quantile="0.75"} 0
|
test_histogram {quantile="0.75"} 0
|
||||||
|
|
@ -84,6 +81,8 @@ test_histogram {quantile="0.95"} 0
|
||||||
test_histogram {quantile="0.99"} 0
|
test_histogram {quantile="0.99"} 0
|
||||||
test_histogram {quantile="0.999"} 0
|
test_histogram {quantile="0.999"} 0
|
||||||
test_histogram {quantile="0.9999"} 0
|
test_histogram {quantile="0.9999"} 0
|
||||||
|
test_histogram_sum 0.000000
|
||||||
|
test_histogram_count 6
|
||||||
|
|
||||||
# TYPE test_meter gauge
|
# TYPE test_meter gauge
|
||||||
test_meter 9999999
|
test_meter 9999999
|
||||||
|
|
@ -99,15 +98,17 @@ test_timer {quantile="0.99"} 1.2e+08
|
||||||
test_timer {quantile="0.999"} 1.2e+08
|
test_timer {quantile="0.999"} 1.2e+08
|
||||||
test_timer {quantile="0.9999"} 1.2e+08
|
test_timer {quantile="0.9999"} 1.2e+08
|
||||||
|
|
||||||
# TYPE test_resetting_timer_count counter
|
|
||||||
test_resetting_timer_count 6
|
|
||||||
|
|
||||||
# TYPE test_resetting_timer summary
|
# TYPE test_resetting_timer summary
|
||||||
test_resetting_timer {quantile="0.50"} 12000000
|
test_resetting_timer {quantile="0.50"} 12000000
|
||||||
test_resetting_timer {quantile="0.95"} 120000000
|
test_resetting_timer {quantile="0.95"} 120000000
|
||||||
test_resetting_timer {quantile="0.99"} 120000000
|
test_resetting_timer {quantile="0.99"} 120000000
|
||||||
|
test_resetting_timer_sum 180000000
|
||||||
|
test_resetting_timer_count 6
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
|
c.addResettingTimer("test/empty_resetting_timer", emptyResettingTimer)
|
||||||
|
|
||||||
exp := c.buff.String()
|
exp := c.buff.String()
|
||||||
if exp != expectedOutput {
|
if exp != expectedOutput {
|
||||||
t.Log("Expected Output:\n", expectedOutput)
|
t.Log("Expected Output:\n", expectedOutput)
|
||||||
|
|
|
||||||
|
|
@ -224,7 +224,7 @@ var (
|
||||||
// Test accounts
|
// Test accounts
|
||||||
testBankKey, _ = crypto.GenerateKey()
|
testBankKey, _ = crypto.GenerateKey()
|
||||||
TestBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
|
TestBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
|
||||||
testBankFunds = big.NewInt(8000000000000000000)
|
testBankFunds = big.NewInt(9000000000000000000)
|
||||||
|
|
||||||
testUserKey, _ = crypto.GenerateKey()
|
testUserKey, _ = crypto.GenerateKey()
|
||||||
testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey)
|
testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey)
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,7 @@ func (b *testWorkerBackend) newRandomTxWithNonce(creation bool, nonce uint64) *t
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
|
||||||
// newRandomTxWithGas creates a new transactionto deploy a storage smart contract.
|
// newRandomTxWithGas creates a new transaction to deploy a storage smart contract.
|
||||||
func (b *testWorkerBackend) newStorageCreateContractTx() (*types.Transaction, common.Address) {
|
func (b *testWorkerBackend) newStorageCreateContractTx() (*types.Transaction, common.Address) {
|
||||||
var tx *types.Transaction
|
var tx *types.Transaction
|
||||||
|
|
||||||
|
|
@ -427,28 +427,13 @@ func (w *worker) mainLoopWithDelay(ctx context.Context, delay uint, opcodeDelay
|
||||||
txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, cmath.FromBig(w.current.header.BaseFee))
|
txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, cmath.FromBig(w.current.header.BaseFee))
|
||||||
tcount := w.current.tcount
|
tcount := w.current.tcount
|
||||||
|
|
||||||
var interruptCtx = context.Background()
|
w.commitTransactions(w.current, txset, nil, context.Background())
|
||||||
stopFn := func() {}
|
|
||||||
defer func() {
|
|
||||||
stopFn()
|
|
||||||
}()
|
|
||||||
|
|
||||||
if w.interruptCommitFlag {
|
|
||||||
block := w.chain.GetBlockByHash(w.chain.CurrentBlock().Hash())
|
|
||||||
interruptCtx, stopFn = getInterruptTimer(ctx, w.current, block)
|
|
||||||
// nolint : staticcheck
|
|
||||||
interruptCtx = vm.PutCache(interruptCtx, w.interruptedTxCache)
|
|
||||||
}
|
|
||||||
|
|
||||||
w.commitTransactionsWithDelay(w.current, txset, nil, interruptCtx)
|
|
||||||
|
|
||||||
// Only update the snapshot if any new transactions were added
|
// Only update the snapshot if any new transactions were added
|
||||||
// to the pending block
|
// to the pending block
|
||||||
if tcount != w.current.tcount {
|
if tcount != w.current.tcount {
|
||||||
w.updateSnapshot(w.current)
|
w.updateSnapshot(w.current)
|
||||||
}
|
}
|
||||||
|
|
||||||
stopFn()
|
|
||||||
} else {
|
} else {
|
||||||
// Special case, if the consensus engine is 0 period clique(dev mode),
|
// Special case, if the consensus engine is 0 period clique(dev mode),
|
||||||
// submit sealing work here since all empty submission will be rejected
|
// submit sealing work here since all empty submission will be rejected
|
||||||
|
|
@ -502,7 +487,7 @@ func (w *worker) commitWorkWithDelay(ctx context.Context, interrupt *int32, noem
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint : contextcheck
|
//nolint:contextcheck
|
||||||
var interruptCtx = context.Background()
|
var interruptCtx = context.Background()
|
||||||
|
|
||||||
stopFn := func() {}
|
stopFn := func() {}
|
||||||
|
|
@ -813,7 +798,6 @@ mainloop:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Start executing the transaction
|
// Start executing the transaction
|
||||||
// TODO marcello check how "Prepare" has been replaced
|
|
||||||
env.state.SetTxContext(tx.Hash(), env.tcount)
|
env.state.SetTxContext(tx.Hash(), env.tcount)
|
||||||
|
|
||||||
var start time.Time
|
var start time.Time
|
||||||
|
|
|
||||||
114
miner/worker.go
114
miner/worker.go
|
|
@ -43,6 +43,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/bor"
|
"github.com/ethereum/go-ethereum/consensus/bor"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/blockstm"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
|
@ -988,6 +989,53 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP
|
||||||
}
|
}
|
||||||
var coalescedLogs []*types.Log
|
var coalescedLogs []*types.Log
|
||||||
|
|
||||||
|
var depsMVReadList [][]blockstm.ReadDescriptor
|
||||||
|
|
||||||
|
var depsMVFullWriteList [][]blockstm.WriteDescriptor
|
||||||
|
|
||||||
|
var mvReadMapList []map[blockstm.Key]blockstm.ReadDescriptor
|
||||||
|
|
||||||
|
var deps map[int]map[int]bool
|
||||||
|
|
||||||
|
chDeps := make(chan blockstm.TxDep)
|
||||||
|
|
||||||
|
var count int
|
||||||
|
|
||||||
|
var depsWg sync.WaitGroup
|
||||||
|
|
||||||
|
var EnableMVHashMap bool
|
||||||
|
|
||||||
|
if w.chainConfig.Bor.IsParallelUniverse(env.header.Number) {
|
||||||
|
EnableMVHashMap = true
|
||||||
|
} else {
|
||||||
|
EnableMVHashMap = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// create and add empty mvHashMap in statedb
|
||||||
|
if EnableMVHashMap {
|
||||||
|
depsMVReadList = [][]blockstm.ReadDescriptor{}
|
||||||
|
|
||||||
|
depsMVFullWriteList = [][]blockstm.WriteDescriptor{}
|
||||||
|
|
||||||
|
mvReadMapList = []map[blockstm.Key]blockstm.ReadDescriptor{}
|
||||||
|
|
||||||
|
deps = map[int]map[int]bool{}
|
||||||
|
|
||||||
|
chDeps = make(chan blockstm.TxDep)
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
depsWg.Add(1)
|
||||||
|
|
||||||
|
go func(chDeps chan blockstm.TxDep) {
|
||||||
|
for t := range chDeps {
|
||||||
|
deps = blockstm.UpdateDeps(deps, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
depsWg.Done()
|
||||||
|
}(chDeps)
|
||||||
|
}
|
||||||
|
|
||||||
initialGasLimit := env.gasPool.Gas()
|
initialGasLimit := env.gasPool.Gas()
|
||||||
initialTxs := txs.GetTxs()
|
initialTxs := txs.GetTxs()
|
||||||
|
|
||||||
|
|
@ -1007,6 +1055,10 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP
|
||||||
mainloop:
|
mainloop:
|
||||||
for {
|
for {
|
||||||
if interruptCtx != nil {
|
if interruptCtx != nil {
|
||||||
|
if EnableMVHashMap {
|
||||||
|
env.state.AddEmptyMVHashMap()
|
||||||
|
}
|
||||||
|
|
||||||
// case of interrupting by timeout
|
// case of interrupting by timeout
|
||||||
select {
|
select {
|
||||||
case <-interruptCtx.Done():
|
case <-interruptCtx.Done():
|
||||||
|
|
@ -1069,6 +1121,22 @@ mainloop:
|
||||||
// Everything ok, collect the logs and shift in the next transaction from the same account
|
// Everything ok, collect the logs and shift in the next transaction from the same account
|
||||||
coalescedLogs = append(coalescedLogs, logs...)
|
coalescedLogs = append(coalescedLogs, logs...)
|
||||||
env.tcount++
|
env.tcount++
|
||||||
|
|
||||||
|
if EnableMVHashMap {
|
||||||
|
depsMVReadList = append(depsMVReadList, env.state.MVReadList())
|
||||||
|
depsMVFullWriteList = append(depsMVFullWriteList, env.state.MVFullWriteList())
|
||||||
|
mvReadMapList = append(mvReadMapList, env.state.MVReadMap())
|
||||||
|
|
||||||
|
temp := blockstm.TxDep{
|
||||||
|
Index: env.tcount - 1,
|
||||||
|
ReadList: depsMVReadList[count],
|
||||||
|
FullWriteList: depsMVFullWriteList,
|
||||||
|
}
|
||||||
|
|
||||||
|
chDeps <- temp
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
|
||||||
txs.Shift()
|
txs.Shift()
|
||||||
|
|
||||||
log.OnDebug(func(lg log.Logging) {
|
log.OnDebug(func(lg log.Logging) {
|
||||||
|
|
@ -1081,6 +1149,50 @@ mainloop:
|
||||||
log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err)
|
log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err)
|
||||||
txs.Pop()
|
txs.Pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if EnableMVHashMap {
|
||||||
|
env.state.ClearReadMap()
|
||||||
|
env.state.ClearWriteMap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint:nestif
|
||||||
|
if EnableMVHashMap {
|
||||||
|
close(chDeps)
|
||||||
|
depsWg.Wait()
|
||||||
|
|
||||||
|
if len(mvReadMapList) > 0 {
|
||||||
|
tempDeps := make([][]uint64, len(mvReadMapList))
|
||||||
|
|
||||||
|
for j := range deps[0] {
|
||||||
|
tempDeps[0] = append(tempDeps[0], uint64(j))
|
||||||
|
}
|
||||||
|
|
||||||
|
delayFlag := true
|
||||||
|
|
||||||
|
for i := 1; i <= len(mvReadMapList)-1; i++ {
|
||||||
|
reads := mvReadMapList[i-1]
|
||||||
|
|
||||||
|
_, ok1 := reads[blockstm.NewSubpathKey(env.coinbase, state.BalancePath)]
|
||||||
|
_, ok2 := reads[blockstm.NewSubpathKey(common.HexToAddress(w.chainConfig.Bor.CalculateBurntContract(env.header.Number.Uint64())), state.BalancePath)]
|
||||||
|
|
||||||
|
if ok1 || ok2 {
|
||||||
|
delayFlag = false
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := range deps[i] {
|
||||||
|
tempDeps[i] = append(tempDeps[i], uint64(j))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if delayFlag {
|
||||||
|
env.header.TxDependency = tempDeps
|
||||||
|
} else {
|
||||||
|
env.header.TxDependency = nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
env.header.TxDependency = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !w.isRunning() && len(coalescedLogs) > 0 {
|
if !w.isRunning() && len(coalescedLogs) > 0 {
|
||||||
// We don't push the pendingLogsEvent while we are sealing. The reason is that
|
// We don't push the pendingLogsEvent while we are sealing. The reason is that
|
||||||
|
|
@ -1493,7 +1605,7 @@ func (w *worker) commitWork(ctx context.Context, interrupt *atomic.Int32, noempt
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint : contextcheck
|
// nolint:contextcheck
|
||||||
var interruptCtx = context.Background()
|
var interruptCtx = context.Background()
|
||||||
|
|
||||||
stopFn := func() {}
|
stopFn := func() {}
|
||||||
|
|
|
||||||
|
|
@ -889,11 +889,6 @@ func BenchmarkBorMining(b *testing.B) {
|
||||||
chain, _ := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, back.Genesis, nil, engine, vm.Config{}, nil, nil, nil)
|
chain, _ := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, back.Genesis, nil, engine, vm.Config{}, nil, nil, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
// Ignore empty commit here for less noise.
|
|
||||||
w.skipSealHook = func(task *task) bool {
|
|
||||||
return len(task.receipts) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// fulfill tx pool
|
// fulfill tx pool
|
||||||
const (
|
const (
|
||||||
totalGas = testGas + params.TxGas
|
totalGas = testGas + params.TxGas
|
||||||
|
|
@ -950,3 +945,126 @@ func BenchmarkBorMining(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// uses core.NewParallelBlockChain to use the dependencies present in the block header
|
||||||
|
// params.BorUnittestChainConfig contains the ParallelUniverseBlock ad big.NewInt(5), so the first 4 blocks will not have metadata.
|
||||||
|
// nolint: gocognit
|
||||||
|
func BenchmarkBorMiningBlockSTMMetadata(b *testing.B) {
|
||||||
|
chainConfig := params.BorUnittestChainConfig
|
||||||
|
|
||||||
|
ctrl := gomock.NewController(b)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
ethAPIMock := api.NewMockCaller(ctrl)
|
||||||
|
ethAPIMock.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||||
|
|
||||||
|
spanner := bor.NewMockSpanner(ctrl)
|
||||||
|
spanner.EXPECT().GetCurrentValidatorsByHash(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*valset.Validator{
|
||||||
|
{
|
||||||
|
ID: 0,
|
||||||
|
Address: TestBankAddress,
|
||||||
|
VotingPower: 100,
|
||||||
|
ProposerPriority: 0,
|
||||||
|
},
|
||||||
|
}, nil).AnyTimes()
|
||||||
|
|
||||||
|
heimdallClientMock := mocks.NewMockIHeimdallClient(ctrl)
|
||||||
|
heimdallClientMock.EXPECT().Close().Times(1)
|
||||||
|
|
||||||
|
contractMock := bor.NewMockGenesisContract(ctrl)
|
||||||
|
|
||||||
|
db, _, _ := NewDBForFakes(b)
|
||||||
|
|
||||||
|
engine := NewFakeBor(b, db, chainConfig, ethAPIMock, spanner, heimdallClientMock, contractMock)
|
||||||
|
defer engine.Close()
|
||||||
|
|
||||||
|
chainConfig.LondonBlock = big.NewInt(0)
|
||||||
|
|
||||||
|
w, back, _ := NewTestWorker(b, chainConfig, engine, db, 0, 0, 0, 0)
|
||||||
|
defer w.close()
|
||||||
|
|
||||||
|
// This test chain imports the mined blocks.
|
||||||
|
db2 := rawdb.NewMemoryDatabase()
|
||||||
|
back.Genesis.MustCommit(db2)
|
||||||
|
|
||||||
|
chain, _ := core.NewParallelBlockChain(db2, nil, back.chain.Config(), engine, vm.Config{ParallelEnable: true, ParallelSpeculativeProcesses: 8}, nil, nil, nil)
|
||||||
|
defer chain.Stop()
|
||||||
|
|
||||||
|
// Ignore empty commit here for less noise.
|
||||||
|
w.skipSealHook = func(task *task) bool {
|
||||||
|
return len(task.receipts) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// fulfill tx pool
|
||||||
|
const (
|
||||||
|
totalGas = testGas + params.TxGas
|
||||||
|
totalBlocks = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
txInBlock := int(back.Genesis.GasLimit/totalGas) + 1
|
||||||
|
|
||||||
|
// a bit risky
|
||||||
|
for i := 0; i < 2*totalBlocks*txInBlock; i++ {
|
||||||
|
err = back.txPool.AddLocal(back.newRandomTx(true))
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal("while adding a local transaction", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = back.txPool.AddLocal(back.newRandomTx(false))
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal("while adding a remote transaction", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for mined blocks.
|
||||||
|
sub := w.mux.Subscribe(core.NewMinedBlockEvent{})
|
||||||
|
defer sub.Unsubscribe()
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
prev := uint64(time.Now().Unix())
|
||||||
|
|
||||||
|
// Start mining!
|
||||||
|
w.start()
|
||||||
|
|
||||||
|
blockPeriod, ok := back.Genesis.Config.Bor.Period["0"]
|
||||||
|
if !ok {
|
||||||
|
blockPeriod = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < totalBlocks; i++ {
|
||||||
|
select {
|
||||||
|
case ev := <-sub.Chan():
|
||||||
|
block := ev.Data.(core.NewMinedBlockEvent).Block
|
||||||
|
|
||||||
|
if _, err := chain.InsertChain([]*types.Block{block}); err != nil {
|
||||||
|
b.Fatalf("failed to insert new mined block %d: %v", block.NumberU64(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for dependencies for block number > 4
|
||||||
|
if block.NumberU64() <= 4 {
|
||||||
|
if block.TxDependency() != nil {
|
||||||
|
b.Fatalf("dependency not nil")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
deps := block.TxDependency()
|
||||||
|
if len(deps[0]) != 0 {
|
||||||
|
b.Fatalf("wrong dependency")
|
||||||
|
}
|
||||||
|
for i := 1; i < block.Transactions().Len(); i++ {
|
||||||
|
if deps[i][0] != uint64(i-1) || len(deps[i]) != 1 {
|
||||||
|
b.Fatalf("wrong dependency")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Log("block", block.NumberU64(), "time", block.Time()-prev, "txs", block.Transactions().Len(), "gasUsed", block.GasUsed(), "gasLimit", block.GasLimit())
|
||||||
|
|
||||||
|
prev = block.Time()
|
||||||
|
case <-time.After(time.Duration(blockPeriod) * time.Second):
|
||||||
|
b.Fatalf("timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ gcmode = "archive"
|
||||||
# etherbase = ""
|
# etherbase = ""
|
||||||
# extradata = ""
|
# extradata = ""
|
||||||
# recommit = "2m5s"
|
# recommit = "2m5s"
|
||||||
|
# commitinterrupt = true
|
||||||
|
|
||||||
[jsonrpc]
|
[jsonrpc]
|
||||||
ipcpath = "/var/lib/bor/bor.ipc"
|
ipcpath = "/var/lib/bor/bor.ipc"
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ syncmode = "full"
|
||||||
# etherbase = ""
|
# etherbase = ""
|
||||||
# extradata = ""
|
# extradata = ""
|
||||||
# recommit = "2m5s"
|
# recommit = "2m5s"
|
||||||
|
# commitinterrupt = true
|
||||||
|
|
||||||
[jsonrpc]
|
[jsonrpc]
|
||||||
ipcpath = "/var/lib/bor/bor.ipc"
|
ipcpath = "/var/lib/bor/bor.ipc"
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ syncmode = "full"
|
||||||
# etherbase = ""
|
# etherbase = ""
|
||||||
# extradata = ""
|
# extradata = ""
|
||||||
# recommit = "2m5s"
|
# recommit = "2m5s"
|
||||||
|
# commitinterrupt = true
|
||||||
|
|
||||||
[jsonrpc]
|
[jsonrpc]
|
||||||
ipcpath = "/var/lib/bor/bor.ipc"
|
ipcpath = "/var/lib/bor/bor.ipc"
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ syncmode = "full"
|
||||||
# etherbase = ""
|
# etherbase = ""
|
||||||
# extradata = ""
|
# extradata = ""
|
||||||
# recommit = "2m5s"
|
# recommit = "2m5s"
|
||||||
|
# commitinterrupt = true
|
||||||
|
|
||||||
[jsonrpc]
|
[jsonrpc]
|
||||||
ipcpath = "/var/lib/bor/bor.ipc"
|
ipcpath = "/var/lib/bor/bor.ipc"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
Source: bor
|
Source: bor
|
||||||
Version: 0.3.9-beta
|
Version: 0.3.9-stable
|
||||||
Section: develop
|
Section: develop
|
||||||
Priority: standard
|
Priority: standard
|
||||||
Maintainer: Polygon <release-team@polygon.technology>
|
Maintainer: Polygon <release-team@polygon.technology>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
Source: bor
|
Source: bor
|
||||||
Version: 0.3.9-beta
|
Version: 0.3.9-stable
|
||||||
Section: develop
|
Section: develop
|
||||||
Priority: standard
|
Priority: standard
|
||||||
Maintainer: Polygon <release-team@polygon.technology>
|
Maintainer: Polygon <release-team@polygon.technology>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
Source: bor-profile
|
Source: bor-profile
|
||||||
Version: 0.3.9-beta
|
Version: 0.3.9-stable
|
||||||
Section: develop
|
Section: develop
|
||||||
Priority: standard
|
Priority: standard
|
||||||
Maintainer: Polygon <release-team@polygon.technology>
|
Maintainer: Polygon <release-team@polygon.technology>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
Source: bor-profile
|
Source: bor-profile
|
||||||
Version: 0.3.9-beta
|
Version: 0.3.9-stable
|
||||||
Section: develop
|
Section: develop
|
||||||
Priority: standard
|
Priority: standard
|
||||||
Maintainer: Polygon <release-team@polygon.technology>
|
Maintainer: Polygon <release-team@polygon.technology>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
Source: bor-profile
|
Source: bor-profile
|
||||||
Version: 0.3.9-beta
|
Version: 0.3.9-stable
|
||||||
Section: develop
|
Section: develop
|
||||||
Priority: standard
|
Priority: standard
|
||||||
Maintainer: Polygon <release-team@polygon.technology>
|
Maintainer: Polygon <release-team@polygon.technology>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
Source: bor-profile
|
Source: bor-profile
|
||||||
Version: 0.3.9-beta
|
Version: 0.3.9-stable
|
||||||
Section: develop
|
Section: develop
|
||||||
Priority: standard
|
Priority: standard
|
||||||
Maintainer: Polygon <release-team@polygon.technology>
|
Maintainer: Polygon <release-team@polygon.technology>
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ gcmode = "archive"
|
||||||
# etherbase = ""
|
# etherbase = ""
|
||||||
# extradata = ""
|
# extradata = ""
|
||||||
# recommit = "2m5s"
|
# recommit = "2m5s"
|
||||||
|
# commitinterrupt = true
|
||||||
|
|
||||||
[jsonrpc]
|
[jsonrpc]
|
||||||
ipcpath = "/var/lib/bor/bor.ipc"
|
ipcpath = "/var/lib/bor/bor.ipc"
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ syncmode = "full"
|
||||||
# etherbase = ""
|
# etherbase = ""
|
||||||
# extradata = ""
|
# extradata = ""
|
||||||
# recommit = "2m5s"
|
# recommit = "2m5s"
|
||||||
|
# commitinterrupt = true
|
||||||
|
|
||||||
[jsonrpc]
|
[jsonrpc]
|
||||||
ipcpath = "/var/lib/bor/bor.ipc"
|
ipcpath = "/var/lib/bor/bor.ipc"
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ syncmode = "full"
|
||||||
# etherbase = ""
|
# etherbase = ""
|
||||||
# extradata = ""
|
# extradata = ""
|
||||||
# recommit = "2m5s"
|
# recommit = "2m5s"
|
||||||
|
# commitinterrupt = true
|
||||||
|
|
||||||
[jsonrpc]
|
[jsonrpc]
|
||||||
ipcpath = "/var/lib/bor/bor.ipc"
|
ipcpath = "/var/lib/bor/bor.ipc"
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ syncmode = "full"
|
||||||
# etherbase = ""
|
# etherbase = ""
|
||||||
# extradata = ""
|
# extradata = ""
|
||||||
# recommit = "2m5s"
|
# recommit = "2m5s"
|
||||||
|
# commitinterrupt = true
|
||||||
|
|
||||||
[jsonrpc]
|
[jsonrpc]
|
||||||
ipcpath = "/var/lib/bor/bor.ipc"
|
ipcpath = "/var/lib/bor/bor.ipc"
|
||||||
|
|
|
||||||
|
|
@ -279,6 +279,7 @@ var (
|
||||||
BerlinBlock: big.NewInt(0),
|
BerlinBlock: big.NewInt(0),
|
||||||
LondonBlock: big.NewInt(0),
|
LondonBlock: big.NewInt(0),
|
||||||
Bor: &BorConfig{
|
Bor: &BorConfig{
|
||||||
|
ParallelUniverseBlock: big.NewInt(5),
|
||||||
Period: map[string]uint64{
|
Period: map[string]uint64{
|
||||||
"0": 1,
|
"0": 1,
|
||||||
},
|
},
|
||||||
|
|
@ -316,8 +317,13 @@ var (
|
||||||
BerlinBlock: big.NewInt(13996000),
|
BerlinBlock: big.NewInt(13996000),
|
||||||
LondonBlock: big.NewInt(22640000),
|
LondonBlock: big.NewInt(22640000),
|
||||||
Bor: &BorConfig{
|
Bor: &BorConfig{
|
||||||
JaipurBlock: big.NewInt(22770000),
|
JaipurBlock: big.NewInt(22770000),
|
||||||
DelhiBlock: big.NewInt(29638656),
|
DelhiBlock: big.NewInt(29638656),
|
||||||
|
ParallelUniverseBlock: big.NewInt(0),
|
||||||
|
IndoreBlock: big.NewInt(36877056),
|
||||||
|
StateSyncConfirmationDelay: map[string]uint64{
|
||||||
|
"36877056": 128,
|
||||||
|
},
|
||||||
Period: map[string]uint64{
|
Period: map[string]uint64{
|
||||||
"0": 2,
|
"0": 2,
|
||||||
"25275000": 5,
|
"25275000": 5,
|
||||||
|
|
@ -369,8 +375,9 @@ var (
|
||||||
BerlinBlock: big.NewInt(14750000),
|
BerlinBlock: big.NewInt(14750000),
|
||||||
LondonBlock: big.NewInt(23850000),
|
LondonBlock: big.NewInt(23850000),
|
||||||
Bor: &BorConfig{
|
Bor: &BorConfig{
|
||||||
JaipurBlock: big.NewInt(23850000),
|
JaipurBlock: big.NewInt(23850000),
|
||||||
DelhiBlock: big.NewInt(38189056),
|
DelhiBlock: big.NewInt(38189056),
|
||||||
|
ParallelUniverseBlock: big.NewInt(0),
|
||||||
Period: map[string]uint64{
|
Period: map[string]uint64{
|
||||||
"0": 2,
|
"0": 2,
|
||||||
},
|
},
|
||||||
|
|
@ -659,17 +666,20 @@ func (c *CliqueConfig) String() string {
|
||||||
|
|
||||||
// BorConfig is the consensus engine configs for Matic bor based sealing.
|
// BorConfig is the consensus engine configs for Matic bor based sealing.
|
||||||
type BorConfig struct {
|
type BorConfig struct {
|
||||||
Period map[string]uint64 `json:"period"` // Number of seconds between blocks to enforce
|
Period map[string]uint64 `json:"period"` // Number of seconds between blocks to enforce
|
||||||
ProducerDelay map[string]uint64 `json:"producerDelay"` // Number of seconds delay between two producer interval
|
ProducerDelay map[string]uint64 `json:"producerDelay"` // Number of seconds delay between two producer interval
|
||||||
Sprint map[string]uint64 `json:"sprint"` // Epoch length to proposer
|
Sprint map[string]uint64 `json:"sprint"` // Epoch length to proposer
|
||||||
BackupMultiplier map[string]uint64 `json:"backupMultiplier"` // Backup multiplier to determine the wiggle time
|
BackupMultiplier map[string]uint64 `json:"backupMultiplier"` // Backup multiplier to determine the wiggle time
|
||||||
ValidatorContract string `json:"validatorContract"` // Validator set contract
|
ValidatorContract string `json:"validatorContract"` // Validator set contract
|
||||||
StateReceiverContract string `json:"stateReceiverContract"` // State receiver contract
|
StateReceiverContract string `json:"stateReceiverContract"` // State receiver contract
|
||||||
OverrideStateSyncRecords map[string]int `json:"overrideStateSyncRecords"` // override state records count
|
OverrideStateSyncRecords map[string]int `json:"overrideStateSyncRecords"` // override state records count
|
||||||
BlockAlloc map[string]interface{} `json:"blockAlloc"`
|
BlockAlloc map[string]interface{} `json:"blockAlloc"`
|
||||||
BurntContract map[string]string `json:"burntContract"` // governance contract where the token will be sent to and burnt in london fork
|
BurntContract map[string]string `json:"burntContract"` // governance contract where the token will be sent to and burnt in london fork
|
||||||
JaipurBlock *big.Int `json:"jaipurBlock"` // Jaipur switch block (nil = no fork, 0 = already on jaipur)
|
JaipurBlock *big.Int `json:"jaipurBlock"` // Jaipur switch block (nil = no fork, 0 = already on jaipur)
|
||||||
DelhiBlock *big.Int `json:"delhiBlock"` // Delhi switch block (nil = no fork, 0 = already on delhi)
|
DelhiBlock *big.Int `json:"delhiBlock"` // Delhi switch block (nil = no fork, 0 = already on delhi)
|
||||||
|
ParallelUniverseBlock *big.Int `json:"parallelUniverseBlock"` // TODO: update all occurrence, change name and finalize number (hardfork for block-stm related changes)
|
||||||
|
IndoreBlock *big.Int `json:"indoreBlock"` // Indore switch block (nil = no fork, 0 = already on indore)
|
||||||
|
StateSyncConfirmationDelay map[string]uint64 `json:"stateSyncConfirmationDelay"` // StateSync Confirmation Delay, in seconds, to calculate `to`
|
||||||
}
|
}
|
||||||
|
|
||||||
// String implements the stringer interface, returning the consensus engine details.
|
// String implements the stringer interface, returning the consensus engine details.
|
||||||
|
|
@ -678,11 +688,11 @@ func (b *BorConfig) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BorConfig) CalculateProducerDelay(number uint64) uint64 {
|
func (c *BorConfig) CalculateProducerDelay(number uint64) uint64 {
|
||||||
return c.calculateSprintSizeHelper(c.ProducerDelay, number)
|
return borKeyValueConfigHelper(c.ProducerDelay, number)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BorConfig) CalculateSprint(number uint64) uint64 {
|
func (c *BorConfig) CalculateSprint(number uint64) uint64 {
|
||||||
return c.calculateSprintSizeHelper(c.Sprint, number)
|
return borKeyValueConfigHelper(c.Sprint, number)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BorConfig) CalculateBackupMultiplier(number uint64) uint64 {
|
func (c *BorConfig) CalculateBackupMultiplier(number uint64) uint64 {
|
||||||
|
|
@ -701,6 +711,23 @@ func (c *BorConfig) IsDelhi(number *big.Int) bool {
|
||||||
return isBlockForked(c.DelhiBlock, number)
|
return isBlockForked(c.DelhiBlock, number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *BorConfig) IsIndore(number *big.Int) bool {
|
||||||
|
return isForked(c.IndoreBlock, number)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BorConfig) CalculateStateSyncDelay(number uint64) uint64 {
|
||||||
|
return borKeyValueConfigHelper(c.StateSyncConfirmationDelay, number)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: modify this function once the block number is finalized
|
||||||
|
func (c *BorConfig) IsParallelUniverse(number *big.Int) bool {
|
||||||
|
if c.ParallelUniverseBlock == big.NewInt(0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return isForked(c.ParallelUniverseBlock, number)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *BorConfig) IsSprintStart(number uint64) bool {
|
func (c *BorConfig) IsSprintStart(number uint64) bool {
|
||||||
return number%c.CalculateSprint(number) == 0
|
return number%c.CalculateSprint(number) == 0
|
||||||
}
|
}
|
||||||
|
|
@ -725,7 +752,7 @@ func (c *BorConfig) calculateBorConfigHelper(field map[string]uint64, number uin
|
||||||
return field[keys[len(keys)-1]]
|
return field[keys[len(keys)-1]]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BorConfig) calculateSprintSizeHelper(field map[string]uint64, number uint64) uint64 {
|
func borKeyValueConfigHelper(field map[string]uint64, number uint64) uint64 {
|
||||||
keys := make([]string, 0, len(field))
|
keys := make([]string, 0, len(field))
|
||||||
for k := range field {
|
for k := range field {
|
||||||
keys = append(keys, k)
|
keys = append(keys, k)
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
VersionMajor = 0 // Major version component of the current release
|
VersionMajor = 0 // Major version component of the current release
|
||||||
VersionMinor = 3 // Minor version component of the current release
|
VersionMinor = 3 // Minor version component of the current release
|
||||||
VersionPatch = 9 // Patch version component of the current release
|
VersionPatch = 9 // Patch version component of the current release
|
||||||
VersionMeta = "beta" // Version metadata to append to the version string
|
VersionMeta = "stable" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
||||||
var GitCommit string
|
var GitCommit string
|
||||||
|
|
@ -43,7 +43,7 @@ var VersionWithMeta = func() string {
|
||||||
return v
|
return v
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// VersionWithMetaCommitDetails holds the textual version string including the metadata and Git Details.
|
// VersionWithCommitDetails holds the textual version string including the metadata and Git Details.
|
||||||
var VersionWithMetaCommitDetails = func() string {
|
var VersionWithMetaCommitDetails = func() string {
|
||||||
v := Version
|
v := Version
|
||||||
if VersionMeta != "" {
|
if VersionMeta != "" {
|
||||||
|
|
|
||||||
81
scripts/updateVersion.sh
Executable file
81
scripts/updateVersion.sh
Executable file
|
|
@ -0,0 +1,81 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||||
|
|
||||||
|
echo "The version is of form - VersionMajor.VersionMinor.VersionPatch-VersionMeta"
|
||||||
|
echo "Let's take 0.3.4-beta as an example. Here:"
|
||||||
|
echo "* VersionMajor is - 0"
|
||||||
|
echo "* VersionMinor is - 3"
|
||||||
|
echo "* VersionPatch is - 4"
|
||||||
|
echo "* VersionMeta is - beta"
|
||||||
|
echo ""
|
||||||
|
echo "Now, enter the new version step-by-step below:"
|
||||||
|
|
||||||
|
version=""
|
||||||
|
|
||||||
|
# VersionMajor
|
||||||
|
read -p "* VersionMajor: " VersionMajor
|
||||||
|
if [ -z "$VersionMajor" ]
|
||||||
|
then
|
||||||
|
echo "VersionMajor cannot be NULL"
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
version+=$VersionMajor
|
||||||
|
|
||||||
|
# VersionMinor
|
||||||
|
read -p "* VersionMinor: " VersionMinor
|
||||||
|
if [ -z "$VersionMinor" ]
|
||||||
|
then
|
||||||
|
echo "VersionMinor cannot be NULL"
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
version+="."$VersionMinor
|
||||||
|
|
||||||
|
# VersionPatch
|
||||||
|
read -p "* VersionPatch: " VersionPatch
|
||||||
|
if [ -z "$VersionPatch" ]
|
||||||
|
then
|
||||||
|
echo "VersionPatch cannot be NULL"
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
version+="."$VersionPatch
|
||||||
|
|
||||||
|
# VersionMeta (optional)
|
||||||
|
read -p "* VersionMeta (optional, press enter if not needed): " VersionMeta
|
||||||
|
if [[ ! -z "$VersionMeta" ]]
|
||||||
|
then
|
||||||
|
version+="-"$VersionMeta
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "New version is: $version"
|
||||||
|
|
||||||
|
# update version in all the 6 templates
|
||||||
|
replace="Version: "$version
|
||||||
|
fileArray=(
|
||||||
|
"${DIR}/../packaging/templates/package_scripts/control"
|
||||||
|
"${DIR}/../packaging/templates/package_scripts/control.arm64"
|
||||||
|
"${DIR}/../packaging/templates/package_scripts/control.profile.amd64"
|
||||||
|
"${DIR}/../packaging/templates/package_scripts/control.profile.arm64"
|
||||||
|
"${DIR}/../packaging/templates/package_scripts/control.validator"
|
||||||
|
"${DIR}/../packaging/templates/package_scripts/control.validator.arm64"
|
||||||
|
)
|
||||||
|
for file in ${fileArray[@]}; do
|
||||||
|
# get the line starting with `Version` in the control file and store it in the $temp variable
|
||||||
|
temp=$(grep "^Version.*" $file)
|
||||||
|
sed -i '' "s%$temp%$replace%" $file
|
||||||
|
done
|
||||||
|
|
||||||
|
# update version in ../params/version.go
|
||||||
|
versionFile="${DIR}/../params/version.go"
|
||||||
|
sed -i '' "s% = .*// Major% = $VersionMajor // Major%g" $versionFile
|
||||||
|
sed -i '' "s% = .*// Minor% = $VersionMinor // Minor%g" $versionFile
|
||||||
|
sed -i '' "s% = .*// Patch% = $VersionPatch // Patch%g" $versionFile
|
||||||
|
sed -i '' "s% = .*// Version metadata% = \"$VersionMeta\" // Version metadata%g" $versionFile
|
||||||
|
gofmt -w $versionFile
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Updating Version Done"
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
|
@ -544,7 +544,7 @@ func TestFetchStateSyncEvents_2(t *testing.T) {
|
||||||
insertNewBlock(t, chain, block)
|
insertNewBlock(t, chain, block)
|
||||||
}
|
}
|
||||||
|
|
||||||
lastStateID, _ := _bor.GenesisContractsClient.LastStateId(sprintSize)
|
lastStateID, _ := _bor.GenesisContractsClient.LastStateId(nil, sprintSize, block.Hash())
|
||||||
|
|
||||||
// state 6 was not written
|
// state 6 was not written
|
||||||
require.Equal(t, uint64(4), lastStateID.Uint64())
|
require.Equal(t, uint64(4), lastStateID.Uint64())
|
||||||
|
|
@ -573,7 +573,7 @@ func TestFetchStateSyncEvents_2(t *testing.T) {
|
||||||
insertNewBlock(t, chain, block)
|
insertNewBlock(t, chain, block)
|
||||||
}
|
}
|
||||||
|
|
||||||
lastStateID, _ = _bor.GenesisContractsClient.LastStateId(spanSize)
|
lastStateID, _ = _bor.GenesisContractsClient.LastStateId(nil, spanSize, block.Hash())
|
||||||
require.Equal(t, uint64(6), lastStateID.Uint64())
|
require.Equal(t, uint64(6), lastStateID.Uint64())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue