mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-21 22:24:32 +00:00
Merge pull request #470 from XinFinOrg/dev-upgrade
Dev upgrade monthly to master
This commit is contained in:
commit
a55f9b5d4c
77 changed files with 2798 additions and 1426 deletions
176
.github/workflows/ci.yml
vendored
Normal file
176
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
GOPROXY: https://proxy.golang.org
|
||||
GO111MODULE: on
|
||||
tf_version: "1.3.0"
|
||||
tf_init_cli_options: "-input=false"
|
||||
tf_validation_cli_options: ""
|
||||
tf_plan_cli_options: "-lock=false -input=false"
|
||||
tf_apply_cli_options: "-auto-approve -input=false"
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
env:
|
||||
GOPATH: ${{ github.workspace }}
|
||||
GOBIN: ${{ github.workspace }}/bin
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ env.GOPATH }}/src/XDPoSChain
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: A-B tests
|
||||
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[a-b].*")
|
||||
- name: C-[a-m] tests
|
||||
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/c[a-m].*")
|
||||
- name: C-[n-o] tests
|
||||
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/c[n-o].*")
|
||||
- name: C-[p-z] tests
|
||||
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/c[p-z].*")
|
||||
- name: D-I tests
|
||||
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[d-i].*")
|
||||
- name: J-N tests
|
||||
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[j-n].*")
|
||||
- name: O-R tests
|
||||
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[o-r].*")
|
||||
- name: S tests
|
||||
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/s.*")
|
||||
- name: T-Z tests
|
||||
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[t-z].*")
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: ${{ env.GOPATH }}/src/XDPoSChain
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
cache: false
|
||||
go-version: '1.21.x'
|
||||
- name: Run tests
|
||||
run: ${{ matrix.script }}
|
||||
env:
|
||||
GO111MODULE: auto
|
||||
|
||||
tag_build:
|
||||
runs-on: ubuntu-latest
|
||||
needs: tests
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Login to Docker Hub
|
||||
run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
|
||||
- name: Build Docker image
|
||||
run: docker build -t xinfinorg/xdposchain:${GITHUB_REF#refs/tags/} -f cicd/Dockerfile .
|
||||
- name: Push Docker image
|
||||
run: docker push xinfinorg/xdposchain:${GITHUB_REF#refs/tags/}
|
||||
|
||||
devnet_build_push:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/')
|
||||
needs: tests
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Login to Docker Hub
|
||||
run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
|
||||
- name: Build and Push Docker images
|
||||
run: |
|
||||
docker pull xinfinorg/devnet:latest
|
||||
docker tag xinfinorg/devnet:latest xinfinorg/devnet:previous
|
||||
docker rmi xinfinorg/devnet:latest
|
||||
docker build -t xinfinorg/devnet:latest -f cicd/Dockerfile .
|
||||
docker push xinfinorg/devnet:latest
|
||||
docker push xinfinorg/devnet:previous
|
||||
|
||||
devnet_terraform_apply:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/')
|
||||
needs: devnet_build_push
|
||||
environment: devnet
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Terraform Apply
|
||||
run: |
|
||||
cd cicd/devnet/terraform
|
||||
terraform init ${{ env.tf_init_cli_options }}
|
||||
terraform apply ${{ env.tf_apply_cli_options }}
|
||||
sleep 5
|
||||
source .env
|
||||
for ((i=$us_east_2_start;i<$us_east_2_end;i++)); do
|
||||
echo "Force deploy xdc-$i"
|
||||
aws ecs update-service --region us-east-2 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager | head -n 10;
|
||||
done
|
||||
for ((i=$eu_west_1_start;i<$eu_west_1_end;i++)); do
|
||||
echo "Force deploy xdc-$i"
|
||||
aws ecs update-service --region eu-west-1 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager | head -n 10;
|
||||
done
|
||||
for ((i=$ap_southeast_2_start;i<$ap_southeast_2_end;i++)); do
|
||||
echo "Force deploy xdc-$i"
|
||||
aws ecs update-service --region ap-southeast-2 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager | head -n 10;
|
||||
done
|
||||
aws ecs update-service --region ap-southeast-1 --cluster devnet-xdcnode-cluster --service ecs-service-rpc1 --force-new-deployment --no-cli-pager | head -n 10;
|
||||
|
||||
rpcnode_terraform_apply:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/')
|
||||
needs: devnet_build_push
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Terraform Apply
|
||||
run: |
|
||||
cd cicd/terraform
|
||||
terraform init ${{ env.tf_init_cli_options }}
|
||||
terraform apply ${{ env.tf_apply_cli_options }}
|
||||
|
||||
testnet_dev-upgrade_node:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/')
|
||||
needs: rpcnode_terraform_apply
|
||||
environment: testnet
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: ECS Update
|
||||
run: |
|
||||
aws ecs update-service --region ap-southeast-1 --cluster testnet-xdcnode-cluster --service ecs-service-testnet-rpc1 --force-new-deployment --no-cli-pager | head -n 10;
|
||||
|
||||
mainnet_dev-upgrade_node:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/')
|
||||
needs: rpcnode_terraform_apply
|
||||
environment: mainnet
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: ECS Update
|
||||
run: |
|
||||
aws ecs update-service --region ap-southeast-1 --cluster mainnet-xdcnode-cluster --service ecs-service-mainnet-rpc1 --force-new-deployment --no-cli-pager | head -n 10;
|
||||
|
||||
devnet_send_notification:
|
||||
runs-on: ubuntu-latest
|
||||
needs: devnet_terraform_apply
|
||||
if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/')
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Send deployment notification
|
||||
run: |
|
||||
curl --location --request POST "66.94.98.186:8080/deploy?environment=devnet&service=xdc&version=${GITHUB_SHA}"
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -50,7 +50,6 @@ profile.cov
|
|||
**/yarn-error.log
|
||||
coverage.txt
|
||||
go.sum
|
||||
cicd/devnet/terraform/.terraform*
|
||||
**/.terraform*
|
||||
cicd/devnet/tmp
|
||||
.env
|
||||
cicd/devnet/terraform/node-config.json
|
||||
|
|
@ -61,7 +61,7 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, er
|
|||
if err != nil {
|
||||
log.Warn("scan get FileInfo", "err", err, "path", path)
|
||||
}
|
||||
if skipKeyFile(fiInfo) {
|
||||
if fiInfo == nil || skipKeyFile(fiInfo) {
|
||||
log.Trace("Ignoring file on account scan", "path", path)
|
||||
continue
|
||||
}
|
||||
|
|
@ -94,7 +94,8 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, er
|
|||
// skipKeyFile ignores editor backups, hidden files and folders/symlinks.
|
||||
func skipKeyFile(fi os.FileInfo) bool {
|
||||
// Skip editor backups and UNIX-style hidden files.
|
||||
if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") {
|
||||
name := fi.Name()
|
||||
if strings.HasSuffix(name, "~") || strings.HasPrefix(name, ".") {
|
||||
return true
|
||||
}
|
||||
// Skip misc special files, directories (yes, symlinks too).
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@ docker build -f cicd/Dockerfile .
|
|||
```
|
||||
## Docker Run
|
||||
```
|
||||
docker run -it -e NETWORK=devnet -e PRIVATE_KEYS=$KEY $IMAGE
|
||||
docker run -it -e NETWORK=devnet -e PRIVATE_KEY=$KEY $IMAGE
|
||||
``
|
||||
|
|
@ -7,10 +7,10 @@ then
|
|||
exit 1
|
||||
fi
|
||||
echo $PRIVATE_KEY >> /tmp/key
|
||||
wallet=$(XDC account import --password .pwd --datadir /work/xdcchain /tmp/key | awk -v FS="({|})" '{print $2}')
|
||||
wallet=$(XDC account import --password .pwd --datadir /work/xdcchain /tmp/key | awk -F '[{}]' '{print $2}')
|
||||
XDC --datadir /work/xdcchain init /work/genesis.json
|
||||
else
|
||||
wallet=$(XDC account list --datadir /work/xdcchain | head -n 1 | awk -v FS="({|})" '{print $2}')
|
||||
wallet=$(XDC account list --datadir /work/xdcchain | head -n 1 | awk -F '[{}]' '{print $2}')
|
||||
fi
|
||||
|
||||
input="/work/bootnodes.list"
|
||||
|
|
@ -80,6 +80,6 @@ XDC --ethstats ${netstats} --gcmode archive \
|
|||
--rpcapi admin,db,eth,debug,net,shh,txpool,personal,web3,XDPoS \
|
||||
--rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \
|
||||
--gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \
|
||||
--periodicprofile --debugdatadir /work/xdcchain \
|
||||
--ws --wsaddr=0.0.0.0 --wsport $ws_port \
|
||||
--debugdatadir /work/xdcchain \
|
||||
--enable-0x-prefix --ws --wsaddr=0.0.0.0 --wsport $ws_port \
|
||||
--wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"name": "tfXdcNode",
|
||||
"image": "xinfinorg/${image_environment}:${image_tag}",
|
||||
"environment": [
|
||||
{"name": "PRIVATE_KEYS", "value": "${private_keys}"},
|
||||
{"name": "PRIVATE_KEY", "value": "${private_key}"},
|
||||
{"name": "LOG_LEVEL", "value": "${log_level}"},
|
||||
{"name": "NODE_NAME", "value": "${node_name}"},
|
||||
{"name": "NETWORK", "value": "${chain_network}"}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ data template_file devnet_container_definition {
|
|||
image_environment = "${lookup(each.value, "imageEnvironment", "devnet")}"
|
||||
image_tag = "${lookup(each.value, "imageTag", "latest")}"
|
||||
node_name = "${each.key}"
|
||||
private_keys = "${each.value.pk}"
|
||||
private_key = "${each.value.pk}"
|
||||
cloudwatch_group = "tf-${each.key}"
|
||||
cloudwatch_region = "${var.region}"
|
||||
log_level = "${lookup(each.value, "logLevel", "${var.logLevel}")}"
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ then
|
|||
exit 1
|
||||
fi
|
||||
echo $PRIVATE_KEY >> /tmp/key
|
||||
wallet=$(XDC account import --password .pwd --datadir /work/xdcchain /tmp/key | awk -v FS="({|})" '{print $2}')
|
||||
wallet=$(XDC account import --password .pwd --datadir /work/xdcchain /tmp/key | awk -F '[{}]' '{print $2}')
|
||||
XDC --datadir /work/xdcchain init /work/genesis.json
|
||||
else
|
||||
wallet=$(XDC account list --datadir /work/xdcchain | head -n 1 | awk -v FS="({|})" '{print $2}')
|
||||
wallet=$(XDC account list --datadir /work/xdcchain | head -n 1 | awk -F '[{}]' '{print $2}')
|
||||
fi
|
||||
|
||||
input="/work/bootnodes.list"
|
||||
|
|
@ -76,9 +76,9 @@ XDC --ethstats ${netstats} --gcmode archive \
|
|||
--datadir /work/xdcchain --networkid 50 \
|
||||
-port $port --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 \
|
||||
--rpcport $rpc_port \
|
||||
--rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,XDPoS \
|
||||
--rpcapi admin,db,eth,debug,net,shh,txpool,personal,web3,XDPoS \
|
||||
--rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \
|
||||
--gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \
|
||||
--periodicprofile --debugdatadir /work/xdcchain \
|
||||
--ws --wsaddr=0.0.0.0 --wsport $ws_port \
|
||||
--debugdatadir /work/xdcchain \
|
||||
--enable-0x-prefix --ws --wsaddr=0.0.0.0 --wsport $ws_port \
|
||||
--wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log
|
||||
|
|
|
|||
13
cicd/terraform/.env
Normal file
13
cicd/terraform/.env
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
log_level=3
|
||||
|
||||
# Ohio
|
||||
us_east_2_start=0
|
||||
us_east_2_end=36
|
||||
|
||||
# Ireland
|
||||
eu_west_1_start=37
|
||||
eu_west_1_end=72
|
||||
|
||||
# Sydney
|
||||
ap_southeast_2_start=73
|
||||
ap_southeast_2_end=108
|
||||
28
cicd/terraform/iam.tf
Normal file
28
cicd/terraform/iam.tf
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# IAM policies
|
||||
data "aws_iam_policy_document" "xdc_ecs_tasks_execution_role" {
|
||||
statement {
|
||||
actions = ["sts:AssumeRole"]
|
||||
|
||||
principals {
|
||||
type = "Service"
|
||||
identifiers = ["ecs-tasks.amazonaws.com"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Create the role
|
||||
resource "aws_iam_role" "xdc_ecs_tasks_execution_role" {
|
||||
name = "xdc-ecs-task-execution-role"
|
||||
assume_role_policy = "${data.aws_iam_policy_document.xdc_ecs_tasks_execution_role.json}"
|
||||
}
|
||||
|
||||
# Attached the AWS managed policies to the new role
|
||||
resource "aws_iam_role_policy_attachment" "xdc_ecs_tasks_execution_role" {
|
||||
for_each = toset([
|
||||
"arn:aws:iam::aws:policy/AmazonElasticFileSystemClientFullAccess",
|
||||
"arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy",
|
||||
"arn:aws:iam::aws:policy/AmazonElasticFileSystemsUtils"
|
||||
])
|
||||
role = aws_iam_role.xdc_ecs_tasks_execution_role.name
|
||||
policy_arn = each.value
|
||||
}
|
||||
59
cicd/terraform/main.tf
Normal file
59
cicd/terraform/main.tf
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
terraform {
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.13.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Default
|
||||
provider "aws" {
|
||||
region = "us-east-1"
|
||||
}
|
||||
|
||||
# WARNING: APSE-1 will only be used to host rpc node
|
||||
# Workaround to avoid conflicts with existing ecs cluster in existing regions
|
||||
provider "aws" {
|
||||
alias = "ap-southeast-1"
|
||||
region = "ap-southeast-1"
|
||||
}
|
||||
|
||||
module "testnet-rpc" {
|
||||
source = "./module/region"
|
||||
region = "ap-southeast-1"
|
||||
nodeKeys = local.rpcTestnetNodeKeys
|
||||
enableFixedIp = true
|
||||
logLevel = local.logLevel
|
||||
xdc_ecs_tasks_execution_role_arn = aws_iam_role.xdc_ecs_tasks_execution_role.arn
|
||||
|
||||
cpu = 1024
|
||||
memory = 4096
|
||||
|
||||
network = "testnet"
|
||||
vpc_cidr = "10.1.0.0/16"
|
||||
subnet_cidr = "10.1.0.0/20"
|
||||
providers = {
|
||||
aws = aws.ap-southeast-1
|
||||
}
|
||||
}
|
||||
|
||||
module "mainnet-rpc" {
|
||||
source = "./module/region"
|
||||
region = "ap-southeast-1"
|
||||
nodeKeys = local.rpcMainnetNodeKeys
|
||||
enableFixedIp = true
|
||||
logLevel = local.logLevel
|
||||
xdc_ecs_tasks_execution_role_arn = aws_iam_role.xdc_ecs_tasks_execution_role.arn
|
||||
|
||||
cpu = 1024
|
||||
memory = 4096
|
||||
|
||||
network = "mainnet"
|
||||
vpc_cidr = "10.2.0.0/16"
|
||||
subnet_cidr = "10.2.0.0/20"
|
||||
providers = {
|
||||
aws = aws.ap-southeast-1
|
||||
}
|
||||
}
|
||||
|
||||
44
cicd/terraform/module/region/container-definition.tpl
Normal file
44
cicd/terraform/module/region/container-definition.tpl
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[
|
||||
{
|
||||
"name": "tfXdcNode",
|
||||
"image": "xinfinorg/${image_environment}:${image_tag}",
|
||||
"environment": [
|
||||
{"name": "PRIVATE_KEY", "value": "${private_key}"},
|
||||
{"name": "LOG_LEVEL", "value": "${log_level}"},
|
||||
{"name": "NODE_NAME", "value": "${node_name}"},
|
||||
{"name": "NETWORK", "value": "${chain_network}"}
|
||||
],
|
||||
"essential": true,
|
||||
"logConfiguration": {
|
||||
"logDriver": "awslogs",
|
||||
"options": {
|
||||
"awslogs-group": "${cloudwatch_group}",
|
||||
"awslogs-region": "${cloudwatch_region}",
|
||||
"awslogs-stream-prefix": "ecs"
|
||||
}
|
||||
},
|
||||
"portMappings": [
|
||||
{
|
||||
"hostPort": 8555,
|
||||
"protocol": "tcp",
|
||||
"containerPort": 8555
|
||||
},
|
||||
{
|
||||
"hostPort": 8545,
|
||||
"protocol": "tcp",
|
||||
"containerPort": 8545
|
||||
},
|
||||
{
|
||||
"hostPort": 30303,
|
||||
"protocol": "tcp",
|
||||
"containerPort": 30303
|
||||
}
|
||||
],
|
||||
"mountPoints": [
|
||||
{
|
||||
"containerPath": "/work/xdcchain",
|
||||
"sourceVolume": "efs"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
96
cicd/terraform/module/region/ecs.tf
Normal file
96
cicd/terraform/module/region/ecs.tf
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
data template_file container_definition {
|
||||
for_each = var.nodeKeys
|
||||
template = "${file("${path.module}/container-definition.tpl")}"
|
||||
|
||||
vars = {
|
||||
image_environment = "${lookup(each.value, "imageEnvironment", "devnet")}"
|
||||
image_tag = "${lookup(each.value, "imageTag", "latest")}"
|
||||
node_name = "${each.key}"
|
||||
private_key = "${each.value.pk}"
|
||||
cloudwatch_group = "tf-${each.key}"
|
||||
cloudwatch_region = "${var.region}"
|
||||
log_level = "${lookup(each.value, "logLevel", "${var.logLevel}")}"
|
||||
chain_network = var.network
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_ecs_task_definition" "task_definition_group" {
|
||||
for_each = var.nodeKeys
|
||||
|
||||
family = "${var.network}-${each.key}"
|
||||
requires_compatibilities = ["FARGATE"]
|
||||
network_mode = "awsvpc"
|
||||
container_definitions = data.template_file.container_definition[each.key].rendered
|
||||
execution_role_arn = var.xdc_ecs_tasks_execution_role_arn
|
||||
task_role_arn = var.xdc_ecs_tasks_execution_role_arn
|
||||
|
||||
# New nodes will consume a lot more CPU usage than existing nodes.
|
||||
# This is due to sync is resource heavy. Recommending set to below if doing sync:
|
||||
# CPU = 2048, Memory = 4096
|
||||
# Please set it back to cpu 256 and memory of 2048 after sync is done to save the cost
|
||||
# cpu = 256
|
||||
# memory = 2048
|
||||
cpu = var.cpu
|
||||
memory = var.memory
|
||||
volume {
|
||||
name = "efs"
|
||||
|
||||
efs_volume_configuration {
|
||||
file_system_id = aws_efs_file_system.efs[each.key].id
|
||||
root_directory = "/"
|
||||
transit_encryption = "ENABLED"
|
||||
authorization_config {
|
||||
access_point_id = aws_efs_access_point.efs_access_point[each.key].id
|
||||
iam = "DISABLED"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tags = {
|
||||
Name = "Tf${var.network}Ecs-${each.key}"
|
||||
}
|
||||
}
|
||||
|
||||
data "aws_ecs_task_definition" "ecs_task_definition" {
|
||||
for_each = var.nodeKeys
|
||||
task_definition = aws_ecs_task_definition.task_definition_group[each.key].family
|
||||
}
|
||||
|
||||
# ECS cluster
|
||||
resource "aws_ecs_cluster" "ecs_cluster" {
|
||||
name = "${var.network}-xdcnode-cluster"
|
||||
tags = {
|
||||
Name = "Tf${var.network}EcsCluster"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
resource "aws_ecs_service" "ecs_service" {
|
||||
for_each = var.enableFixedIp ? {} : var.nodeKeys
|
||||
name = "ecs-service-${each.key}"
|
||||
cluster = aws_ecs_cluster.ecs_cluster.id
|
||||
task_definition = "${aws_ecs_task_definition.task_definition_group[each.key].family}:${max(aws_ecs_task_definition.task_definition_group[each.key].revision, data.aws_ecs_task_definition.ecs_task_definition[each.key].revision)}"
|
||||
launch_type = "FARGATE"
|
||||
scheduling_strategy = "REPLICA"
|
||||
desired_count = 1
|
||||
force_new_deployment = true
|
||||
deployment_minimum_healthy_percent = 0
|
||||
deployment_maximum_percent = 100
|
||||
|
||||
network_configuration {
|
||||
subnets = [aws_subnet.subnet.id]
|
||||
assign_public_ip = true
|
||||
security_groups = [
|
||||
aws_default_security_group.xdcnode_security_group.id
|
||||
]
|
||||
}
|
||||
|
||||
deployment_circuit_breaker {
|
||||
enable = true
|
||||
rollback = false
|
||||
}
|
||||
|
||||
tags = {
|
||||
Name = "Tf${var.network}EcsService-${each.key}"
|
||||
}
|
||||
}
|
||||
67
cicd/terraform/module/region/efs.tf
Normal file
67
cicd/terraform/module/region/efs.tf
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
|
||||
# EFS
|
||||
resource "aws_security_group" "efs_security_group" {
|
||||
name = "Tf${var.network}EfsSecurityGroup"
|
||||
description = "Allow HTTP in and out of ${var.network} EFS"
|
||||
vpc_id = aws_vpc.vpc.id
|
||||
|
||||
ingress {
|
||||
from_port = 2049
|
||||
to_port = 2049
|
||||
protocol = "TCP"
|
||||
security_groups = [aws_default_security_group.xdcnode_security_group.id]
|
||||
}
|
||||
|
||||
egress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
tags = {
|
||||
Name = "Tf${var.network}Efs"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_efs_file_system" "efs" {
|
||||
for_each = var.nodeKeys
|
||||
creation_token = "efs-${each.key}"
|
||||
performance_mode = "generalPurpose"
|
||||
throughput_mode = "bursting"
|
||||
encrypted = "true"
|
||||
lifecycle_policy {
|
||||
transition_to_ia = "AFTER_30_DAYS"
|
||||
}
|
||||
tags = {
|
||||
Name = "Tf${var.network}Efs${each.key}"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_efs_mount_target" "efs_efs_mount_target" {
|
||||
for_each = var.nodeKeys
|
||||
file_system_id = aws_efs_file_system.efs[each.key].id
|
||||
subnet_id = aws_subnet.subnet.id
|
||||
security_groups = [aws_security_group.efs_security_group.id]
|
||||
}
|
||||
|
||||
resource "aws_efs_access_point" "efs_access_point" {
|
||||
for_each = var.nodeKeys
|
||||
file_system_id = aws_efs_file_system.efs[each.key].id
|
||||
root_directory {
|
||||
path = "/${each.key}/database"
|
||||
creation_info {
|
||||
owner_gid = 1001
|
||||
owner_uid = 1001
|
||||
permissions = 777
|
||||
}
|
||||
}
|
||||
posix_user {
|
||||
gid = 1001
|
||||
uid = 1001
|
||||
secondary_gids = [0]
|
||||
}
|
||||
|
||||
tags = {
|
||||
Name = "Tf${var.network}EfsAccessPoint${each.key}"
|
||||
}
|
||||
}
|
||||
103
cicd/terraform/module/region/main.tf
Normal file
103
cicd/terraform/module/region/main.tf
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
terraform {
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.13.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_vpc" "vpc" {
|
||||
cidr_block = var.vpc_cidr
|
||||
instance_tenancy = "default"
|
||||
enable_dns_hostnames = true
|
||||
|
||||
tags = {
|
||||
Name = "Tf${var.network}Vpc"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_subnet" "subnet" {
|
||||
vpc_id = aws_vpc.vpc.id
|
||||
cidr_block = var.subnet_cidr
|
||||
map_public_ip_on_launch = true
|
||||
|
||||
tags = {
|
||||
Name = "Tf${var.network}VpcSubnet"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_internet_gateway" "gatewat" {
|
||||
vpc_id = aws_vpc.vpc.id
|
||||
|
||||
tags = {
|
||||
Name = "Tf${var.network}Gateway"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_route_table" "route_table" {
|
||||
vpc_id = aws_vpc.vpc.id
|
||||
|
||||
route {
|
||||
cidr_block = "0.0.0.0/0"
|
||||
gateway_id = aws_internet_gateway.gatewat.id
|
||||
}
|
||||
|
||||
tags = {
|
||||
Name = "Tf${var.network}VpcRoutingTable"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_route_table_association" "route_table_association" {
|
||||
subnet_id = aws_subnet.subnet.id
|
||||
route_table_id = aws_route_table.route_table.id
|
||||
}
|
||||
|
||||
resource "aws_default_security_group" "xdcnode_security_group" {
|
||||
vpc_id = aws_vpc.vpc.id
|
||||
|
||||
ingress {
|
||||
description = "listener port"
|
||||
from_port = 30303
|
||||
to_port = 30303
|
||||
protocol = "tcp"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
ingress {
|
||||
description = "discovery port"
|
||||
from_port = 30303
|
||||
to_port = 30303
|
||||
protocol = "udp"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
ingress {
|
||||
description = "rpc port"
|
||||
from_port = 8545
|
||||
to_port = 8545
|
||||
protocol = "tcp"
|
||||
cidr_blocks = [var.vpc_cidr]
|
||||
}
|
||||
|
||||
egress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
tags = {
|
||||
Name = "Tf${var.network}Node"
|
||||
}
|
||||
}
|
||||
|
||||
# Logs
|
||||
resource "aws_cloudwatch_log_group" "cloud_watch_group" {
|
||||
for_each = var.nodeKeys
|
||||
|
||||
name = "tf-${each.key}"
|
||||
retention_in_days = 14 # Logs are only kept for 14 days
|
||||
tags = {
|
||||
Name = "Tf${var.network}CloudWatchGroup${each.key}"
|
||||
}
|
||||
}
|
||||
104
cicd/terraform/module/region/rpc.tf
Normal file
104
cicd/terraform/module/region/rpc.tf
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
# Allocate an Elastic IP for the NLB
|
||||
resource "aws_eip" "nlb_eip" {
|
||||
domain = "vpc"
|
||||
}
|
||||
|
||||
|
||||
# Create a Network Load Balancer
|
||||
resource "aws_lb" "rpc_node_nlb" {
|
||||
count = var.enableFixedIp ? 1 : 0
|
||||
name = "${var.network}-rpc-node-nlb"
|
||||
load_balancer_type = "network"
|
||||
|
||||
enable_deletion_protection = false
|
||||
|
||||
subnet_mapping {
|
||||
subnet_id = aws_subnet.subnet.id
|
||||
allocation_id = aws_eip.nlb_eip.id
|
||||
}
|
||||
}
|
||||
|
||||
# Listener and Target Group for the rpc node container
|
||||
resource "aws_lb_target_group" "rpc_node_tg_8545" {
|
||||
count = var.enableFixedIp ? 1 : 0
|
||||
name = "${var.network}-rpc-node-tg"
|
||||
port = 8545
|
||||
protocol = "TCP"
|
||||
vpc_id = aws_vpc.vpc.id
|
||||
target_type = "ip"
|
||||
}
|
||||
|
||||
resource "aws_lb_listener" "rpc_node_listener_8545" {
|
||||
count = var.enableFixedIp ? 1 : 0
|
||||
load_balancer_arn = aws_lb.rpc_node_nlb[0].arn
|
||||
port = 8545
|
||||
protocol = "TCP"
|
||||
|
||||
default_action {
|
||||
type = "forward"
|
||||
target_group_arn = aws_lb_target_group.rpc_node_tg_8545[0].arn
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_ecs_service" "rpc_node_ecs_service" {
|
||||
for_each = var.enableFixedIp ? var.nodeKeys : {}
|
||||
name = "ecs-service-${each.key}"
|
||||
cluster = aws_ecs_cluster.ecs_cluster.id
|
||||
task_definition = "${aws_ecs_task_definition.task_definition_group[each.key].family}:${max(aws_ecs_task_definition.task_definition_group[each.key].revision, data.aws_ecs_task_definition.ecs_task_definition[each.key].revision)}"
|
||||
launch_type = "FARGATE"
|
||||
scheduling_strategy = "REPLICA"
|
||||
desired_count = 1
|
||||
force_new_deployment = true
|
||||
deployment_minimum_healthy_percent = 0
|
||||
deployment_maximum_percent = 100
|
||||
|
||||
network_configuration {
|
||||
subnets = [aws_subnet.subnet.id]
|
||||
assign_public_ip = true
|
||||
security_groups = [
|
||||
aws_default_security_group.xdcnode_security_group.id
|
||||
]
|
||||
}
|
||||
|
||||
deployment_circuit_breaker {
|
||||
enable = true
|
||||
rollback = false
|
||||
}
|
||||
|
||||
load_balancer {
|
||||
target_group_arn = aws_lb_target_group.rpc_node_tg_8545[0].arn
|
||||
container_name = "tfXdcNode"
|
||||
container_port = 8545
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
aws_lb_listener.rpc_node_listener_8545
|
||||
]
|
||||
|
||||
tags = {
|
||||
Name = "TfRpcNodeEcsService-${each.key}"
|
||||
}
|
||||
}
|
||||
|
||||
# Target Group for port 30303
|
||||
resource "aws_lb_target_group" "rpc_node_tg_30303" {
|
||||
count = var.enableFixedIp ? 1 : 0
|
||||
name = "${var.network}-rpc-node-tg-30303"
|
||||
port = 30303
|
||||
protocol = "TCP"
|
||||
vpc_id = aws_vpc.vpc.id
|
||||
target_type = "ip"
|
||||
}
|
||||
|
||||
# Listener for port 30303
|
||||
resource "aws_lb_listener" "rpc_node_listener_30303" {
|
||||
count = var.enableFixedIp ? 1 : 0
|
||||
load_balancer_arn = aws_lb.rpc_node_nlb[0].arn
|
||||
port = 30303
|
||||
protocol = "TCP"
|
||||
|
||||
default_action {
|
||||
type = "forward"
|
||||
target_group_arn = aws_lb_target_group.rpc_node_tg_30303[0].arn
|
||||
}
|
||||
}
|
||||
50
cicd/terraform/module/region/variables.tf
Normal file
50
cicd/terraform/module/region/variables.tf
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
variable "region" {
|
||||
description = "AWS region"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "nodeKeys" {
|
||||
description = "each miner's key"
|
||||
type = map
|
||||
}
|
||||
|
||||
variable "logLevel" {
|
||||
description = "containers log level"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "xdc_ecs_tasks_execution_role_arn" {
|
||||
description = "aws iam role resource arn"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "enableFixedIp" {
|
||||
description = "a flag to indicate whether fixed ip should be associated to the nodes. This is used for RPC node"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "network" {
|
||||
description = "blockchain network"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "cpu" {
|
||||
description = "container cpu"
|
||||
type = number
|
||||
}
|
||||
|
||||
variable "memory" {
|
||||
description = "container memory"
|
||||
type = number
|
||||
}
|
||||
|
||||
variable "vpc_cidr" {
|
||||
description = "vpc cidr"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "subnet_cidr" {
|
||||
description = "subnet cidr"
|
||||
type = string
|
||||
}
|
||||
14
cicd/terraform/s3.tf
Normal file
14
cicd/terraform/s3.tf
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Bucket need to be created first. If first time run terraform init, need to comment out the below section
|
||||
terraform {
|
||||
backend "s3" {
|
||||
bucket = "tf-xinfin-bucket"
|
||||
key = "tf/terraform.tfstate"
|
||||
region = "us-east-1"
|
||||
encrypt = true
|
||||
}
|
||||
}
|
||||
|
||||
data "aws_s3_object" "xdc_node_config" {
|
||||
bucket = "tf-xinfin-bucket"
|
||||
key = "node-config.json"
|
||||
}
|
||||
35
cicd/terraform/variables.tf
Normal file
35
cicd/terraform/variables.tf
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
locals {
|
||||
/**
|
||||
Load the nodes data from s3
|
||||
Below is the the format the config needs to follow:
|
||||
{{Name of the node, in a pattern of 'xdc'+ number. i.e xdc50}}: {
|
||||
pk: {{Value of the node private key}},
|
||||
... any other configuration we want to pass.
|
||||
}
|
||||
Note: No `n` is allowed in the node name
|
||||
**/
|
||||
predefinedNodesConfig = jsondecode(data.aws_s3_object.xdc_node_config.body)
|
||||
envs = { for tuple in regexall("(.*)=(.*)", file(".env")) : tuple[0] => tuple[1] }
|
||||
logLevel = local.envs["log_level"]
|
||||
|
||||
# regions = [
|
||||
# {
|
||||
# "name": "us-east-2", // Ohio
|
||||
# "start": local.envs["us_east_2_start"],
|
||||
# "end": local.envs["us_east_2_end"],
|
||||
# }
|
||||
# ]
|
||||
|
||||
# keyNames = {
|
||||
# for r in local.regions :
|
||||
# r.name => [for i in range(r.start, r.end+1) : "xdc${i}"]
|
||||
# }
|
||||
|
||||
# nodeKeys = {
|
||||
# for r in local.regions :
|
||||
# r.name => { for i in local.keyNames[r.name]: i => local.predefinedNodesConfig[i] }
|
||||
# }
|
||||
|
||||
rpcTestnetNodeKeys = { "testnet-rpc1": local.predefinedNodesConfig["testnet-rpc1"]} // we hardcode the rpc to a single node for now
|
||||
rpcMainnetNodeKeys = { "mainnet-rpc1": local.predefinedNodesConfig["mainnet-rpc1"]} // we hardcode the rpc to a single node for now
|
||||
}
|
||||
|
|
@ -8,10 +8,10 @@ then
|
|||
exit 1
|
||||
fi
|
||||
echo $PRIVATE_KEY >> /tmp/key
|
||||
wallet=$(XDC account import --password .pwd --datadir /work/xdcchain /tmp/key | awk -v FS="({|})" '{print $2}')
|
||||
wallet=$(XDC account import --password .pwd --datadir /work/xdcchain /tmp/key | awk -F '[{}]' '{print $2}')
|
||||
XDC --datadir /work/xdcchain init /work/genesis.json
|
||||
else
|
||||
wallet=$(XDC account list --datadir /work/xdcchain | head -n 1 | awk -v FS="({|})" '{print $2}')
|
||||
wallet=$(XDC account list --datadir /work/xdcchain | head -n 1 | awk -F '[{}]' '{print $2}')
|
||||
fi
|
||||
|
||||
input="/work/bootnodes.list"
|
||||
|
|
@ -78,9 +78,9 @@ XDC --ethstats ${netstats} --gcmode archive \
|
|||
--datadir /work/xdcchain --networkid 51 \
|
||||
-port $port --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 \
|
||||
--rpcport $rpc_port \
|
||||
--rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,XDPoS \
|
||||
--rpcapi admin,db,eth,debug,net,shh,txpool,personal,web3,XDPoS \
|
||||
--rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \
|
||||
--gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \
|
||||
--periodicprofile --debugdatadir /work/xdcchain \
|
||||
--ws --wsaddr=0.0.0.0 --wsport $ws_port \
|
||||
--debugdatadir /work/xdcchain \
|
||||
--enable-0x-prefix --ws --wsaddr=0.0.0.0 --wsport $ws_port \
|
||||
--wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log
|
||||
|
|
|
|||
|
|
@ -319,7 +319,8 @@ func exportChain(ctx *cli.Context) error {
|
|||
utils.Fatalf("This command requires an argument.")
|
||||
}
|
||||
stack, _ := makeFullNode(ctx)
|
||||
chain, _ := utils.MakeChain(ctx, stack)
|
||||
chain, db := utils.MakeChain(ctx, stack)
|
||||
defer db.Close()
|
||||
start := time.Now()
|
||||
|
||||
var err error
|
||||
|
|
@ -353,6 +354,7 @@ func importPreimages(ctx *cli.Context) error {
|
|||
}
|
||||
stack, _ := makeFullNode(ctx)
|
||||
diskdb := utils.MakeChainDatabase(ctx, stack)
|
||||
defer diskdb.Close()
|
||||
|
||||
start := time.Now()
|
||||
if err := utils.ImportPreimages(diskdb, ctx.Args().First()); err != nil {
|
||||
|
|
@ -369,6 +371,7 @@ func exportPreimages(ctx *cli.Context) error {
|
|||
}
|
||||
stack, _ := makeFullNode(ctx)
|
||||
diskdb := utils.MakeChainDatabase(ctx, stack)
|
||||
defer diskdb.Close()
|
||||
|
||||
start := time.Now()
|
||||
if err := utils.ExportPreimages(diskdb, ctx.Args().First()); err != nil {
|
||||
|
|
@ -386,6 +389,7 @@ func copyDb(ctx *cli.Context) error {
|
|||
// Initialize a new chain for the running node to sync into
|
||||
stack, _ := makeFullNode(ctx)
|
||||
chain, chainDb := utils.MakeChain(ctx, stack)
|
||||
defer chainDb.Close()
|
||||
|
||||
syncmode := *utils.GlobalTextMarshaler(ctx, utils.SyncModeFlag.Name).(*downloader.SyncMode)
|
||||
dl := downloader.New(syncmode, chainDb, new(event.TypeMux), chain, nil, nil, nil)
|
||||
|
|
@ -458,6 +462,8 @@ func removeDB(ctx *cli.Context) error {
|
|||
func dump(ctx *cli.Context) error {
|
||||
stack, _ := makeFullNode(ctx)
|
||||
chain, chainDb := utils.MakeChain(ctx, stack)
|
||||
defer chainDb.Close()
|
||||
|
||||
for _, arg := range ctx.Args() {
|
||||
var block *types.Block
|
||||
if hashish(arg) {
|
||||
|
|
@ -477,7 +483,6 @@ func dump(ctx *cli.Context) error {
|
|||
fmt.Printf("%s\n", state.Dump())
|
||||
}
|
||||
}
|
||||
chainDb.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,12 @@ func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cos
|
|||
log.Memory = memory.Data()
|
||||
}
|
||||
if !l.cfg.DisableStack {
|
||||
log.Stack = stack.Data()
|
||||
//TODO(@holiman) improve this
|
||||
logstack := make([]*big.Int, len(stack.Data()))
|
||||
for i, item := range stack.Data() {
|
||||
logstack[i] = item.ToBig()
|
||||
}
|
||||
log.Stack = logstack
|
||||
}
|
||||
return l.encoder.Encode(log)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,11 @@ var TIPXDCX = big.NewInt(38383838)
|
|||
var TIPXDCXLending = big.NewInt(38383838)
|
||||
var TIPXDCXCancellationFee = big.NewInt(38383838)
|
||||
var TIPXDCXCancellationFeeTestnet = big.NewInt(38383838)
|
||||
var TIPXDCXDISABLE = big.NewInt(99999999900)
|
||||
var BerlinBlock = big.NewInt(9999999999)
|
||||
var LondonBlock = big.NewInt(9999999999)
|
||||
var MergeBlock = big.NewInt(9999999999)
|
||||
var ShanghaiBlock = big.NewInt(9999999999)
|
||||
|
||||
var TIPXDCXTestnet = big.NewInt(38383838)
|
||||
var IsTestnet bool = false
|
||||
|
|
|
|||
|
|
@ -45,6 +45,11 @@ var TIPXDCX = big.NewInt(225000)
|
|||
var TIPXDCXLending = big.NewInt(225000)
|
||||
var TIPXDCXCancellationFee = big.NewInt(225000)
|
||||
var TIPXDCXCancellationFeeTestnet = big.NewInt(225000)
|
||||
var TIPXDCXDISABLE = big.NewInt(15894900)
|
||||
var BerlinBlock = big.NewInt(9999999999)
|
||||
var LondonBlock = big.NewInt(9999999999)
|
||||
var MergeBlock = big.NewInt(9999999999)
|
||||
var ShanghaiBlock = big.NewInt(16832700)
|
||||
|
||||
var TIPXDCXTestnet = big.NewInt(0)
|
||||
var IsTestnet bool = false
|
||||
|
|
|
|||
|
|
@ -45,6 +45,11 @@ var TIPXDCX = big.NewInt(23779191)
|
|||
var TIPXDCXLending = big.NewInt(23779191)
|
||||
var TIPXDCXCancellationFee = big.NewInt(23779191)
|
||||
var TIPXDCXCancellationFeeTestnet = big.NewInt(23779191)
|
||||
var TIPXDCXDISABLE = big.NewInt(99999999900)
|
||||
var BerlinBlock = big.NewInt(9999999999)
|
||||
var LondonBlock = big.NewInt(9999999999)
|
||||
var MergeBlock = big.NewInt(9999999999)
|
||||
var ShanghaiBlock = big.NewInt(9999999999)
|
||||
|
||||
var TIPXDCXTestnet = big.NewInt(23779191)
|
||||
var IsTestnet bool = false
|
||||
|
|
|
|||
223
common/lru/basiclru.go
Normal file
223
common/lru/basiclru.go
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
// Copyright 2022 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 lru implements generically-typed LRU caches.
|
||||
package lru
|
||||
|
||||
// BasicLRU is a simple LRU cache.
|
||||
//
|
||||
// This type is not safe for concurrent use.
|
||||
// The zero value is not valid, instances must be created using NewCache.
|
||||
type BasicLRU[K comparable, V any] struct {
|
||||
list *list[K]
|
||||
items map[K]cacheItem[K, V]
|
||||
cap int
|
||||
}
|
||||
|
||||
type cacheItem[K any, V any] struct {
|
||||
elem *listElem[K]
|
||||
value V
|
||||
}
|
||||
|
||||
// NewBasicLRU creates a new LRU cache.
|
||||
func NewBasicLRU[K comparable, V any](capacity int) BasicLRU[K, V] {
|
||||
if capacity <= 0 {
|
||||
capacity = 1
|
||||
}
|
||||
c := BasicLRU[K, V]{
|
||||
items: make(map[K]cacheItem[K, V]),
|
||||
list: newList[K](),
|
||||
cap: capacity,
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Add adds a value to the cache. Returns true if an item was evicted to store the new item.
|
||||
func (c *BasicLRU[K, V]) Add(key K, value V) (evicted bool) {
|
||||
item, ok := c.items[key]
|
||||
if ok {
|
||||
// Already exists in cache.
|
||||
item.value = value
|
||||
c.items[key] = item
|
||||
c.list.moveToFront(item.elem)
|
||||
return false
|
||||
}
|
||||
|
||||
var elem *listElem[K]
|
||||
if c.Len() >= c.cap {
|
||||
elem = c.list.removeLast()
|
||||
delete(c.items, elem.v)
|
||||
evicted = true
|
||||
} else {
|
||||
elem = new(listElem[K])
|
||||
}
|
||||
|
||||
// Store the new item.
|
||||
// Note that, if another item was evicted, we re-use its list element here.
|
||||
elem.v = key
|
||||
c.items[key] = cacheItem[K, V]{elem, value}
|
||||
c.list.pushElem(elem)
|
||||
return evicted
|
||||
}
|
||||
|
||||
// Contains reports whether the given key exists in the cache.
|
||||
func (c *BasicLRU[K, V]) Contains(key K) bool {
|
||||
_, ok := c.items[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Get retrieves a value from the cache. This marks the key as recently used.
|
||||
func (c *BasicLRU[K, V]) Get(key K) (value V, ok bool) {
|
||||
item, ok := c.items[key]
|
||||
if !ok {
|
||||
return value, false
|
||||
}
|
||||
c.list.moveToFront(item.elem)
|
||||
return item.value, true
|
||||
}
|
||||
|
||||
// GetOldest retrieves the least-recently-used item.
|
||||
// Note that this does not update the item's recency.
|
||||
func (c *BasicLRU[K, V]) GetOldest() (key K, value V, ok bool) {
|
||||
lastElem := c.list.last()
|
||||
if lastElem == nil {
|
||||
return key, value, false
|
||||
}
|
||||
key = lastElem.v
|
||||
item := c.items[key]
|
||||
return key, item.value, true
|
||||
}
|
||||
|
||||
// Len returns the current number of items in the cache.
|
||||
func (c *BasicLRU[K, V]) Len() int {
|
||||
return len(c.items)
|
||||
}
|
||||
|
||||
// Peek retrieves a value from the cache, but does not mark the key as recently used.
|
||||
func (c *BasicLRU[K, V]) Peek(key K) (value V, ok bool) {
|
||||
item, ok := c.items[key]
|
||||
return item.value, ok
|
||||
}
|
||||
|
||||
// Purge empties the cache.
|
||||
func (c *BasicLRU[K, V]) Purge() {
|
||||
c.list.init()
|
||||
for k := range c.items {
|
||||
delete(c.items, k)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove drops an item from the cache. Returns true if the key was present in cache.
|
||||
func (c *BasicLRU[K, V]) Remove(key K) bool {
|
||||
item, ok := c.items[key]
|
||||
if ok {
|
||||
delete(c.items, key)
|
||||
c.list.remove(item.elem)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// RemoveOldest drops the least recently used item.
|
||||
func (c *BasicLRU[K, V]) RemoveOldest() (key K, value V, ok bool) {
|
||||
lastElem := c.list.last()
|
||||
if lastElem == nil {
|
||||
return key, value, false
|
||||
}
|
||||
|
||||
key = lastElem.v
|
||||
item := c.items[key]
|
||||
delete(c.items, key)
|
||||
c.list.remove(lastElem)
|
||||
return key, item.value, true
|
||||
}
|
||||
|
||||
// Keys returns all keys in the cache.
|
||||
func (c *BasicLRU[K, V]) Keys() []K {
|
||||
keys := make([]K, 0, len(c.items))
|
||||
return c.list.appendTo(keys)
|
||||
}
|
||||
|
||||
// list is a doubly-linked list holding items of type he.
|
||||
// The zero value is not valid, use newList to create lists.
|
||||
type list[T any] struct {
|
||||
root listElem[T]
|
||||
}
|
||||
|
||||
type listElem[T any] struct {
|
||||
next *listElem[T]
|
||||
prev *listElem[T]
|
||||
v T
|
||||
}
|
||||
|
||||
func newList[T any]() *list[T] {
|
||||
l := new(list[T])
|
||||
l.init()
|
||||
return l
|
||||
}
|
||||
|
||||
// init reinitializes the list, making it empty.
|
||||
func (l *list[T]) init() {
|
||||
l.root.next = &l.root
|
||||
l.root.prev = &l.root
|
||||
}
|
||||
|
||||
// push adds an element to the front of the list.
|
||||
func (l *list[T]) pushElem(e *listElem[T]) {
|
||||
e.prev = &l.root
|
||||
e.next = l.root.next
|
||||
l.root.next = e
|
||||
e.next.prev = e
|
||||
}
|
||||
|
||||
// moveToFront makes 'node' the head of the list.
|
||||
func (l *list[T]) moveToFront(e *listElem[T]) {
|
||||
e.prev.next = e.next
|
||||
e.next.prev = e.prev
|
||||
l.pushElem(e)
|
||||
}
|
||||
|
||||
// remove removes an element from the list.
|
||||
func (l *list[T]) remove(e *listElem[T]) {
|
||||
e.prev.next = e.next
|
||||
e.next.prev = e.prev
|
||||
e.next, e.prev = nil, nil
|
||||
}
|
||||
|
||||
// removeLast removes the last element of the list.
|
||||
func (l *list[T]) removeLast() *listElem[T] {
|
||||
last := l.last()
|
||||
if last != nil {
|
||||
l.remove(last)
|
||||
}
|
||||
return last
|
||||
}
|
||||
|
||||
// last returns the last element of the list, or nil if the list is empty.
|
||||
func (l *list[T]) last() *listElem[T] {
|
||||
e := l.root.prev
|
||||
if e == &l.root {
|
||||
return nil
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// appendTo appends all list elements to a slice.
|
||||
func (l *list[T]) appendTo(slice []T) []T {
|
||||
for e := l.root.prev; e != &l.root; e = e.prev {
|
||||
slice = append(slice, e.v)
|
||||
}
|
||||
return slice
|
||||
}
|
||||
255
common/lru/basiclru_test.go
Normal file
255
common/lru/basiclru_test.go
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
// Copyright 2022 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 lru
|
||||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Some of these test cases were adapted
|
||||
// from https://github.com/hashicorp/golang-lru/blob/master/simplelru/lru_test.go
|
||||
|
||||
func TestBasicLRU(t *testing.T) {
|
||||
cache := NewBasicLRU[int, int](128)
|
||||
|
||||
for i := 0; i < 256; i++ {
|
||||
cache.Add(i, i)
|
||||
}
|
||||
if cache.Len() != 128 {
|
||||
t.Fatalf("bad len: %v", cache.Len())
|
||||
}
|
||||
|
||||
// Check that Keys returns least-recent key first.
|
||||
keys := cache.Keys()
|
||||
if len(keys) != 128 {
|
||||
t.Fatal("wrong Keys() length", len(keys))
|
||||
}
|
||||
for i, k := range keys {
|
||||
v, ok := cache.Peek(k)
|
||||
if !ok {
|
||||
t.Fatalf("expected key %d be present", i)
|
||||
}
|
||||
if v != k {
|
||||
t.Fatalf("expected %d == %d", k, v)
|
||||
}
|
||||
if v != i+128 {
|
||||
t.Fatalf("wrong value at key %d: %d, want %d", i, v, i+128)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < 128; i++ {
|
||||
_, ok := cache.Get(i)
|
||||
if ok {
|
||||
t.Fatalf("%d should be evicted", i)
|
||||
}
|
||||
}
|
||||
for i := 128; i < 256; i++ {
|
||||
_, ok := cache.Get(i)
|
||||
if !ok {
|
||||
t.Fatalf("%d should not be evicted", i)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 128; i < 192; i++ {
|
||||
ok := cache.Remove(i)
|
||||
if !ok {
|
||||
t.Fatalf("%d should be in cache", i)
|
||||
}
|
||||
ok = cache.Remove(i)
|
||||
if ok {
|
||||
t.Fatalf("%d should not be in cache", i)
|
||||
}
|
||||
_, ok = cache.Get(i)
|
||||
if ok {
|
||||
t.Fatalf("%d should be deleted", i)
|
||||
}
|
||||
}
|
||||
|
||||
// Request item 192.
|
||||
cache.Get(192)
|
||||
// It should be the last item returned by Keys().
|
||||
for i, k := range cache.Keys() {
|
||||
if (i < 63 && k != i+193) || (i == 63 && k != 192) {
|
||||
t.Fatalf("out of order key: %v", k)
|
||||
}
|
||||
}
|
||||
|
||||
cache.Purge()
|
||||
if cache.Len() != 0 {
|
||||
t.Fatalf("bad len: %v", cache.Len())
|
||||
}
|
||||
if _, ok := cache.Get(200); ok {
|
||||
t.Fatalf("should contain nothing")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasicLRUAddExistingKey(t *testing.T) {
|
||||
cache := NewBasicLRU[int, int](1)
|
||||
|
||||
cache.Add(1, 1)
|
||||
cache.Add(1, 2)
|
||||
|
||||
v, _ := cache.Get(1)
|
||||
if v != 2 {
|
||||
t.Fatal("wrong value:", v)
|
||||
}
|
||||
}
|
||||
|
||||
// This test checks GetOldest and RemoveOldest.
|
||||
func TestBasicLRUGetOldest(t *testing.T) {
|
||||
cache := NewBasicLRU[int, int](128)
|
||||
for i := 0; i < 256; i++ {
|
||||
cache.Add(i, i)
|
||||
}
|
||||
|
||||
k, _, ok := cache.GetOldest()
|
||||
if !ok {
|
||||
t.Fatalf("missing")
|
||||
}
|
||||
if k != 128 {
|
||||
t.Fatalf("bad: %v", k)
|
||||
}
|
||||
|
||||
k, _, ok = cache.RemoveOldest()
|
||||
if !ok {
|
||||
t.Fatalf("missing")
|
||||
}
|
||||
if k != 128 {
|
||||
t.Fatalf("bad: %v", k)
|
||||
}
|
||||
|
||||
k, _, ok = cache.RemoveOldest()
|
||||
if !ok {
|
||||
t.Fatalf("missing oldest item")
|
||||
}
|
||||
if k != 129 {
|
||||
t.Fatalf("wrong oldest item: %v", k)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that Add returns true/false if an eviction occurred
|
||||
func TestBasicLRUAddReturnValue(t *testing.T) {
|
||||
cache := NewBasicLRU[int, int](1)
|
||||
if cache.Add(1, 1) {
|
||||
t.Errorf("first add shouldn't have evicted")
|
||||
}
|
||||
if !cache.Add(2, 2) {
|
||||
t.Errorf("second add should have evicted")
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies that Contains doesn't change item recency.
|
||||
func TestBasicLRUContains(t *testing.T) {
|
||||
cache := NewBasicLRU[int, int](2)
|
||||
cache.Add(1, 1)
|
||||
cache.Add(2, 2)
|
||||
if !cache.Contains(1) {
|
||||
t.Errorf("1 should be in the cache")
|
||||
}
|
||||
cache.Add(3, 3)
|
||||
if cache.Contains(1) {
|
||||
t.Errorf("Contains should not have updated recency of 1")
|
||||
}
|
||||
}
|
||||
|
||||
// Test that Peek doesn't update recent-ness
|
||||
func TestBasicLRUPeek(t *testing.T) {
|
||||
cache := NewBasicLRU[int, int](2)
|
||||
cache.Add(1, 1)
|
||||
cache.Add(2, 2)
|
||||
if v, ok := cache.Peek(1); !ok || v != 1 {
|
||||
t.Errorf("1 should be set to 1")
|
||||
}
|
||||
cache.Add(3, 3)
|
||||
if cache.Contains(1) {
|
||||
t.Errorf("should not have updated recent-ness of 1")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLRU(b *testing.B) {
|
||||
var (
|
||||
capacity = 1000
|
||||
indexes = make([]int, capacity*20)
|
||||
keys = make([]string, capacity)
|
||||
values = make([][]byte, capacity)
|
||||
)
|
||||
for i := range indexes {
|
||||
indexes[i] = rand.Intn(capacity)
|
||||
}
|
||||
for i := range keys {
|
||||
b := make([]byte, 32)
|
||||
crand.Read(b)
|
||||
keys[i] = string(b)
|
||||
crand.Read(b)
|
||||
values[i] = b
|
||||
}
|
||||
|
||||
var sink []byte
|
||||
|
||||
b.Run("Add/BasicLRU", func(b *testing.B) {
|
||||
cache := NewBasicLRU[int, int](capacity)
|
||||
for i := 0; i < b.N; i++ {
|
||||
cache.Add(i, i)
|
||||
}
|
||||
})
|
||||
b.Run("Get/BasicLRU", func(b *testing.B) {
|
||||
cache := NewBasicLRU[string, []byte](capacity)
|
||||
for i := 0; i < capacity; i++ {
|
||||
index := indexes[i]
|
||||
cache.Add(keys[index], values[index])
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
k := keys[indexes[i%len(indexes)]]
|
||||
v, ok := cache.Get(k)
|
||||
if ok {
|
||||
sink = v
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// // vs. github.com/hashicorp/golang-lru/simplelru
|
||||
// b.Run("Add/simplelru.LRU", func(b *testing.B) {
|
||||
// cache, _ := simplelru.NewLRU(capacity, nil)
|
||||
// for i := 0; i < b.N; i++ {
|
||||
// cache.Add(i, i)
|
||||
// }
|
||||
// })
|
||||
// b.Run("Get/simplelru.LRU", func(b *testing.B) {
|
||||
// cache, _ := simplelru.NewLRU(capacity, nil)
|
||||
// for i := 0; i < capacity; i++ {
|
||||
// index := indexes[i]
|
||||
// cache.Add(keys[index], values[index])
|
||||
// }
|
||||
//
|
||||
// b.ResetTimer()
|
||||
// for i := 0; i < b.N; i++ {
|
||||
// k := keys[indexes[i%len(indexes)]]
|
||||
// v, ok := cache.Get(k)
|
||||
// if ok {
|
||||
// sink = v.([]byte)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
|
||||
fmt.Fprintln(io.Discard, sink)
|
||||
}
|
||||
84
common/lru/blob_lru.go
Normal file
84
common/lru/blob_lru.go
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2022 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 lru
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// blobType is the type constraint for values stored in SizeConstrainedCache.
|
||||
type blobType interface {
|
||||
~[]byte | ~string
|
||||
}
|
||||
|
||||
// SizeConstrainedCache is a cache where capacity is in bytes (instead of item count). When the cache
|
||||
// is at capacity, and a new item is added, older items are evicted until the size
|
||||
// constraint is met.
|
||||
//
|
||||
// OBS: This cache assumes that items are content-addressed: keys are unique per content.
|
||||
// In other words: two Add(..) with the same key K, will always have the same value V.
|
||||
type SizeConstrainedCache[K comparable, V blobType] struct {
|
||||
size uint64
|
||||
maxSize uint64
|
||||
lru BasicLRU[K, V]
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// NewSizeConstrainedCache creates a new size-constrained LRU cache.
|
||||
func NewSizeConstrainedCache[K comparable, V blobType](maxSize uint64) *SizeConstrainedCache[K, V] {
|
||||
return &SizeConstrainedCache[K, V]{
|
||||
size: 0,
|
||||
maxSize: maxSize,
|
||||
lru: NewBasicLRU[K, V](math.MaxInt),
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds a value to the cache. Returns true if an eviction occurred.
|
||||
// OBS: This cache assumes that items are content-addressed: keys are unique per content.
|
||||
// In other words: two Add(..) with the same key K, will always have the same value V.
|
||||
// OBS: The value is _not_ copied on Add, so the caller must not modify it afterwards.
|
||||
func (c *SizeConstrainedCache[K, V]) Add(key K, value V) (evicted bool) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
// Unless it is already present, might need to evict something.
|
||||
// OBS: If it is present, we still call Add internally to bump the recentness.
|
||||
if !c.lru.Contains(key) {
|
||||
targetSize := c.size + uint64(len(value))
|
||||
for targetSize > c.maxSize {
|
||||
evicted = true
|
||||
_, v, ok := c.lru.RemoveOldest()
|
||||
if !ok {
|
||||
// list is now empty. Break
|
||||
break
|
||||
}
|
||||
targetSize -= uint64(len(v))
|
||||
}
|
||||
c.size = targetSize
|
||||
}
|
||||
c.lru.Add(key, value)
|
||||
return evicted
|
||||
}
|
||||
|
||||
// Get looks up a key's value from the cache.
|
||||
func (c *SizeConstrainedCache[K, V]) Get(key K) (V, bool) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
return c.lru.Get(key)
|
||||
}
|
||||
155
common/lru/blob_lru_test.go
Normal file
155
common/lru/blob_lru_test.go
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
// Copyright 2022 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 lru
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testKey [8]byte
|
||||
|
||||
func mkKey(i int) (key testKey) {
|
||||
binary.LittleEndian.PutUint64(key[:], uint64(i))
|
||||
return key
|
||||
}
|
||||
|
||||
func TestSizeConstrainedCache(t *testing.T) {
|
||||
lru := NewSizeConstrainedCache[testKey, []byte](100)
|
||||
var want uint64
|
||||
// Add 11 items of 10 byte each. First item should be swapped out
|
||||
for i := 0; i < 11; i++ {
|
||||
k := mkKey(i)
|
||||
v := fmt.Sprintf("value-%04d", i)
|
||||
lru.Add(k, []byte(v))
|
||||
want += uint64(len(v))
|
||||
if want > 100 {
|
||||
want = 100
|
||||
}
|
||||
if have := lru.size; have != want {
|
||||
t.Fatalf("size wrong, have %d want %d", have, want)
|
||||
}
|
||||
}
|
||||
// Zero:th should be evicted
|
||||
{
|
||||
k := mkKey(0)
|
||||
if _, ok := lru.Get(k); ok {
|
||||
t.Fatalf("should be evicted: %v", k)
|
||||
}
|
||||
}
|
||||
// Elems 1-11 should be present
|
||||
for i := 1; i < 11; i++ {
|
||||
k := mkKey(i)
|
||||
want := fmt.Sprintf("value-%04d", i)
|
||||
have, ok := lru.Get(k)
|
||||
if !ok {
|
||||
t.Fatalf("missing key %v", k)
|
||||
}
|
||||
if string(have) != want {
|
||||
t.Fatalf("wrong value, have %v want %v", have, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This test adds inserting an element exceeding the max size.
|
||||
func TestSizeConstrainedCacheOverflow(t *testing.T) {
|
||||
lru := NewSizeConstrainedCache[testKey, []byte](100)
|
||||
|
||||
// Add 10 items of 10 byte each, filling the cache
|
||||
for i := 0; i < 10; i++ {
|
||||
k := mkKey(i)
|
||||
v := fmt.Sprintf("value-%04d", i)
|
||||
lru.Add(k, []byte(v))
|
||||
}
|
||||
// Add one single large elem. We expect it to swap out all entries.
|
||||
{
|
||||
k := mkKey(1337)
|
||||
v := make([]byte, 200)
|
||||
lru.Add(k, v)
|
||||
}
|
||||
// Elems 0-9 should be missing
|
||||
for i := 1; i < 10; i++ {
|
||||
k := mkKey(i)
|
||||
if _, ok := lru.Get(k); ok {
|
||||
t.Fatalf("should be evicted: %v", k)
|
||||
}
|
||||
}
|
||||
// The size should be accurate
|
||||
if have, want := lru.size, uint64(200); have != want {
|
||||
t.Fatalf("size wrong, have %d want %d", have, want)
|
||||
}
|
||||
// Adding one small item should swap out the large one
|
||||
{
|
||||
i := 0
|
||||
k := mkKey(i)
|
||||
v := fmt.Sprintf("value-%04d", i)
|
||||
lru.Add(k, []byte(v))
|
||||
if have, want := lru.size, uint64(10); have != want {
|
||||
t.Fatalf("size wrong, have %d want %d", have, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This checks what happens when inserting the same k/v multiple times.
|
||||
func TestSizeConstrainedCacheSameItem(t *testing.T) {
|
||||
lru := NewSizeConstrainedCache[testKey, []byte](100)
|
||||
|
||||
// Add one 10 byte-item 10 times.
|
||||
k := mkKey(0)
|
||||
v := fmt.Sprintf("value-%04d", 0)
|
||||
for i := 0; i < 10; i++ {
|
||||
lru.Add(k, []byte(v))
|
||||
}
|
||||
|
||||
// The size should be accurate.
|
||||
if have, want := lru.size, uint64(10); have != want {
|
||||
t.Fatalf("size wrong, have %d want %d", have, want)
|
||||
}
|
||||
}
|
||||
|
||||
// This tests that empty/nil values are handled correctly.
|
||||
func TestSizeConstrainedCacheEmpties(t *testing.T) {
|
||||
lru := NewSizeConstrainedCache[testKey, []byte](100)
|
||||
|
||||
// This test abuses the lru a bit, using different keys for identical value(s).
|
||||
for i := 0; i < 10; i++ {
|
||||
lru.Add(testKey{byte(i)}, []byte{})
|
||||
lru.Add(testKey{byte(255 - i)}, nil)
|
||||
}
|
||||
|
||||
// The size should not count, only the values count. So this could be a DoS
|
||||
// since it basically has no cap, and it is intentionally overloaded with
|
||||
// different-keyed 0-length values.
|
||||
if have, want := lru.size, uint64(0); have != want {
|
||||
t.Fatalf("size wrong, have %d want %d", have, want)
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
if v, ok := lru.Get(testKey{byte(i)}); !ok {
|
||||
t.Fatalf("test %d: expected presence", i)
|
||||
} else if v == nil {
|
||||
t.Fatalf("test %d, v is nil", i)
|
||||
}
|
||||
|
||||
if v, ok := lru.Get(testKey{byte(255 - i)}); !ok {
|
||||
t.Fatalf("test %d: expected presence", i)
|
||||
} else if v != nil {
|
||||
t.Fatalf("test %d, v is not nil", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
95
common/lru/lru.go
Normal file
95
common/lru/lru.go
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2022 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 lru
|
||||
|
||||
import "sync"
|
||||
|
||||
// Cache is a LRU cache.
|
||||
// This type is safe for concurrent use.
|
||||
type Cache[K comparable, V any] struct {
|
||||
cache BasicLRU[K, V]
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// NewCache creates an LRU cache.
|
||||
func NewCache[K comparable, V any](capacity int) *Cache[K, V] {
|
||||
return &Cache[K, V]{cache: NewBasicLRU[K, V](capacity)}
|
||||
}
|
||||
|
||||
// Add adds a value to the cache. Returns true if an item was evicted to store the new item.
|
||||
func (c *Cache[K, V]) Add(key K, value V) (evicted bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
return c.cache.Add(key, value)
|
||||
}
|
||||
|
||||
// Contains reports whether the given key exists in the cache.
|
||||
func (c *Cache[K, V]) Contains(key K) bool {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
return c.cache.Contains(key)
|
||||
}
|
||||
|
||||
// Get retrieves a value from the cache. This marks the key as recently used.
|
||||
func (c *Cache[K, V]) Get(key K) (value V, ok bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
return c.cache.Get(key)
|
||||
}
|
||||
|
||||
// Len returns the current number of items in the cache.
|
||||
func (c *Cache[K, V]) Len() int {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
return c.cache.Len()
|
||||
}
|
||||
|
||||
// Peek retrieves a value from the cache, but does not mark the key as recently used.
|
||||
func (c *Cache[K, V]) Peek(key K) (value V, ok bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
return c.cache.Peek(key)
|
||||
}
|
||||
|
||||
// Purge empties the cache.
|
||||
func (c *Cache[K, V]) Purge() {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.cache.Purge()
|
||||
}
|
||||
|
||||
// Remove drops an item from the cache. Returns true if the key was present in cache.
|
||||
func (c *Cache[K, V]) Remove(key K) bool {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
return c.cache.Remove(key)
|
||||
}
|
||||
|
||||
// Keys returns all keys of items currently in the LRU.
|
||||
func (c *Cache[K, V]) Keys() []K {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
return c.cache.Keys()
|
||||
}
|
||||
|
|
@ -708,9 +708,9 @@ func (x *XDPoS_v1) Prepare(chain consensus.ChainReader, header *types.Header) er
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if number%x.config.Epoch != 0 {
|
||||
x.lock.RLock()
|
||||
|
||||
x.lock.RLock()
|
||||
if number%x.config.Epoch != 0 {
|
||||
// Gather all the proposals that make sense voting on
|
||||
addresses := make([]common.Address, 0, len(x.proposals))
|
||||
for address, authorize := range x.proposals {
|
||||
|
|
@ -727,14 +727,16 @@ func (x *XDPoS_v1) Prepare(chain consensus.ChainReader, header *types.Header) er
|
|||
copy(header.Nonce[:], utils.NonceDropVote)
|
||||
}
|
||||
}
|
||||
x.lock.RUnlock()
|
||||
}
|
||||
signer := x.signer
|
||||
x.lock.RUnlock()
|
||||
|
||||
parent := chain.GetHeader(header.ParentHash, number-1)
|
||||
if parent == nil {
|
||||
return consensus.ErrUnknownAncestor
|
||||
}
|
||||
// Set the correct difficulty
|
||||
header.Difficulty = x.calcDifficulty(chain, parent, x.signer)
|
||||
header.Difficulty = x.calcDifficulty(chain, parent, signer)
|
||||
log.Debug("CalcDifficulty ", "number", header.Number, "difficulty", header.Difficulty)
|
||||
// Ensure the extra data has all it's components
|
||||
if len(header.Extra) < utils.ExtraVanity {
|
||||
|
|
@ -956,7 +958,10 @@ func (x *XDPoS_v1) Seal(chain consensus.ChainReader, block *types.Block, stop <-
|
|||
// that a new block should have based on the previous blocks in the chain and the
|
||||
// current signer.
|
||||
func (x *XDPoS_v1) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
|
||||
return x.calcDifficulty(chain, parent, x.signer)
|
||||
x.lock.RLock()
|
||||
signer := x.signer
|
||||
x.lock.RUnlock()
|
||||
return x.calcDifficulty(chain, parent, signer)
|
||||
}
|
||||
|
||||
func (x *XDPoS_v1) calcDifficulty(chain consensus.ChainReader, parent *types.Header, signer common.Address) *big.Int {
|
||||
|
|
|
|||
|
|
@ -123,6 +123,8 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time
|
|||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(signatures))
|
||||
|
||||
var mutex sync.Mutex
|
||||
var haveError error
|
||||
|
||||
signedTimeoutObj := types.TimeoutSigHash(&types.TimeoutForSign{
|
||||
|
|
@ -134,15 +136,19 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time
|
|||
go func(sig types.Signature) {
|
||||
defer wg.Done()
|
||||
verified, _, err := x.verifyMsgSignature(signedTimeoutObj, sig, snap.NextEpochMasterNodes)
|
||||
if err != nil {
|
||||
log.Error("[verifyTC] Error while verfying TC message signatures", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(signatures), "Error", err)
|
||||
haveError = fmt.Errorf("error while verfying TC message signatures, %s", err)
|
||||
return
|
||||
}
|
||||
if !verified {
|
||||
log.Warn("[verifyTC] Signature not verified doing TC verification", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(signatures))
|
||||
haveError = fmt.Errorf("fail to verify TC due to signature mis-match")
|
||||
return
|
||||
if err != nil || !verified {
|
||||
log.Error("[verifyTC] Error or verification failure", "Signature", sig, "Error", err)
|
||||
mutex.Lock() // Lock before accessing haveError
|
||||
if haveError == nil {
|
||||
if err != nil {
|
||||
log.Error("[verifyTC] Error while verfying TC message signatures", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(signatures), "Error", err)
|
||||
haveError = fmt.Errorf("error while verifying TC message signatures, %s", err)
|
||||
} else {
|
||||
log.Warn("[verifyTC] Signature not verified doing TC verification", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(signatures))
|
||||
haveError = fmt.Errorf("fail to verify TC due to signature mis-match")
|
||||
}
|
||||
}
|
||||
mutex.Unlock() // Unlock after modifying haveError
|
||||
}
|
||||
}(signature)
|
||||
}
|
||||
|
|
@ -193,6 +199,7 @@ func (x *XDPoS_v2) sendTimeout(chain consensus.ChainReader) error {
|
|||
epochSwitchInfo, err := x.getEpochSwitchInfo(chain, currentBlockHeader, currentBlockHeader.Hash())
|
||||
if err != nil {
|
||||
log.Error("[sendTimeout] Error when trying to get current epoch switch info for a non-epoch block", "currentRound", x.currentRound, "currentBlockNum", currentBlockHeader.Number, "currentBlockHash", currentBlockHeader.Hash(), "epochNum", epochNum)
|
||||
return err
|
||||
}
|
||||
gapNumber = epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64() - epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64()%x.config.Epoch - x.config.Gap
|
||||
log.Debug("[sendTimeout] non-epoch-switch block found its epoch block and calculated the gapNumber", "epochSwitchInfo.EpochSwitchBlockInfo.Number", epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64(), "gapNumber", gapNumber)
|
||||
|
|
|
|||
|
|
@ -22,53 +22,37 @@ import (
|
|||
"encoding/hex"
|
||||
)
|
||||
|
||||
// Tests disassembling the instructions for valid evm code
|
||||
func TestInstructionIteratorValid(t *testing.T) {
|
||||
cnt := 0
|
||||
script, _ := hex.DecodeString("61000000")
|
||||
// Tests disassembling instructions
|
||||
func TestInstructionIterator(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
want int
|
||||
code string
|
||||
wantErr string
|
||||
}{
|
||||
{2, "61000000", ""}, // valid code
|
||||
{0, "6100", "incomplete push instruction at 0"}, // invalid code
|
||||
{2, "5900", ""}, // push0
|
||||
{0, "", ""}, // empty
|
||||
|
||||
it := NewInstructionIterator(script)
|
||||
for it.Next() {
|
||||
cnt++
|
||||
}
|
||||
|
||||
if err := it.Error(); err != nil {
|
||||
t.Errorf("Expected 2, but encountered error %v instead.", err)
|
||||
}
|
||||
if cnt != 2 {
|
||||
t.Errorf("Expected 2, but got %v instead.", cnt)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests disassembling the instructions for invalid evm code
|
||||
func TestInstructionIteratorInvalid(t *testing.T) {
|
||||
cnt := 0
|
||||
script, _ := hex.DecodeString("6100")
|
||||
|
||||
it := NewInstructionIterator(script)
|
||||
for it.Next() {
|
||||
cnt++
|
||||
}
|
||||
|
||||
if it.Error() == nil {
|
||||
t.Errorf("Expected an error, but got %v instead.", cnt)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests disassembling the instructions for empty evm code
|
||||
func TestInstructionIteratorEmpty(t *testing.T) {
|
||||
cnt := 0
|
||||
script, _ := hex.DecodeString("")
|
||||
|
||||
it := NewInstructionIterator(script)
|
||||
for it.Next() {
|
||||
cnt++
|
||||
}
|
||||
|
||||
if err := it.Error(); err != nil {
|
||||
t.Errorf("Expected 0, but encountered error %v instead.", err)
|
||||
}
|
||||
if cnt != 0 {
|
||||
t.Errorf("Expected 0, but got %v instead.", cnt)
|
||||
} {
|
||||
var (
|
||||
have int
|
||||
code, _ = hex.DecodeString(tc.code)
|
||||
it = NewInstructionIterator(code)
|
||||
)
|
||||
for it.Next() {
|
||||
have++
|
||||
}
|
||||
var haveErr = ""
|
||||
if it.Error() != nil {
|
||||
haveErr = it.Error().Error()
|
||||
}
|
||||
if haveErr != tc.wantErr {
|
||||
t.Errorf("test %d: encountered error: %q want %q", i, haveErr, tc.wantErr)
|
||||
continue
|
||||
}
|
||||
if have != tc.want {
|
||||
t.Errorf("wrong instruction count, have %d want %d", have, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,17 +23,23 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
)
|
||||
|
||||
// NewEVMContext creates a new context for use in the EVM.
|
||||
func NewEVMContext(msg Message, header *types.Header, chain consensus.ChainContext, author *common.Address) vm.Context {
|
||||
// If we don't have an explicit author (i.e. not mining), extract from the header
|
||||
var beneficiary common.Address
|
||||
var (
|
||||
beneficiary common.Address
|
||||
random common.Hash
|
||||
)
|
||||
if author == nil {
|
||||
beneficiary, _ = chain.Engine().Author(header) // Ignore error, we're past header validation
|
||||
} else {
|
||||
beneficiary = *author
|
||||
}
|
||||
// since xdpos chain do not use difficulty and mixdigest, we use hash of the block number as random
|
||||
random = crypto.Keccak256Hash(header.Number.Bytes())
|
||||
return vm.Context{
|
||||
CanTransfer: CanTransfer,
|
||||
Transfer: Transfer,
|
||||
|
|
@ -45,6 +51,7 @@ func NewEVMContext(msg Message, header *types.Header, chain consensus.ChainConte
|
|||
Difficulty: new(big.Int).Set(header.Difficulty),
|
||||
GasLimit: header.GasLimit,
|
||||
GasPrice: new(big.Int).Set(msg.GasPrice()),
|
||||
Random: &random,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -306,9 +306,6 @@ func (self *stateObject) setBalance(amount *big.Int) {
|
|||
}
|
||||
}
|
||||
|
||||
// Return the gas back to the origin. Used by the Virtual machine or Closures
|
||||
func (c *stateObject) ReturnGas(gas *big.Int) {}
|
||||
|
||||
func (self *stateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *stateObject {
|
||||
stateObject := newObject(db, self.address, self.data, onDirty)
|
||||
if self.trie != nil {
|
||||
|
|
@ -396,6 +393,10 @@ func (self *stateObject) Nonce() uint64 {
|
|||
return self.data.Nonce
|
||||
}
|
||||
|
||||
func (self *stateObject) Root() common.Hash {
|
||||
return self.data.Root
|
||||
}
|
||||
|
||||
// Never called, but must be present to allow stateObject to be used
|
||||
// as a vm.Account interface that also satisfies the vm.ContractRef
|
||||
// interface. Interfaces are awesome.
|
||||
|
|
|
|||
|
|
@ -83,6 +83,14 @@ type StateDB struct {
|
|||
lock sync.Mutex
|
||||
}
|
||||
|
||||
type AccountInfo struct {
|
||||
CodeSize int
|
||||
Nonce uint64
|
||||
Balance *big.Int
|
||||
CodeHash common.Hash
|
||||
StorageHash common.Hash
|
||||
}
|
||||
|
||||
func (self *StateDB) SubRefund(gas uint64) {
|
||||
self.journal = append(self.journal, refundChange{
|
||||
prev: self.refund})
|
||||
|
|
@ -221,6 +229,16 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 {
|
|||
return 0
|
||||
}
|
||||
|
||||
// GetStorageRoot retrieves the storage root from the given address or empty
|
||||
// if object not found.
|
||||
func (self *StateDB) GetStorageRoot(addr common.Address) common.Hash {
|
||||
stateObject := self.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return stateObject.Root()
|
||||
}
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
func (self *StateDB) GetCode(addr common.Address) []byte {
|
||||
stateObject := self.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
|
|
@ -252,6 +270,28 @@ func (self *StateDB) GetCodeHash(addr common.Address) common.Hash {
|
|||
return common.BytesToHash(stateObject.CodeHash())
|
||||
}
|
||||
|
||||
func (self *StateDB) GetAccountInfo(addr common.Address) *AccountInfo {
|
||||
result := AccountInfo{}
|
||||
|
||||
stateObject := self.getStateObject(addr)
|
||||
if stateObject == nil {
|
||||
result.Balance = common.Big0
|
||||
return &result
|
||||
}
|
||||
|
||||
if stateObject.code != nil {
|
||||
result.CodeSize = len(stateObject.code)
|
||||
} else {
|
||||
result.CodeSize, _ = self.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash()))
|
||||
}
|
||||
result.Nonce = stateObject.Nonce()
|
||||
result.Balance = stateObject.Balance()
|
||||
result.CodeHash = common.BytesToHash(stateObject.CodeHash())
|
||||
result.StorageHash = stateObject.Root()
|
||||
|
||||
return &result
|
||||
}
|
||||
|
||||
func (self *StateDB) GetState(addr common.Address, bhash common.Hash) common.Hash {
|
||||
stateObject := self.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
|
|
|
|||
|
|
@ -14,17 +14,17 @@ type Signature []byte
|
|||
|
||||
// Block Info struct in XDPoS 2.0, used for vote message, etc.
|
||||
type BlockInfo struct {
|
||||
Hash common.Hash
|
||||
Round Round
|
||||
Number *big.Int
|
||||
Hash common.Hash `json:"hash"`
|
||||
Round Round `json:"round"`
|
||||
Number *big.Int `json:"number"`
|
||||
}
|
||||
|
||||
// Vote message in XDPoS 2.0
|
||||
type Vote struct {
|
||||
signer common.Address
|
||||
ProposedBlockInfo *BlockInfo
|
||||
Signature Signature
|
||||
GapNumber uint64
|
||||
signer common.Address //field not exported
|
||||
ProposedBlockInfo *BlockInfo `json:"proposedBlockInfo"`
|
||||
Signature Signature `json:"signature"`
|
||||
GapNumber uint64 `json:"gapNumber"`
|
||||
}
|
||||
|
||||
func (v *Vote) Hash() common.Hash {
|
||||
|
|
@ -81,9 +81,9 @@ func (s *SyncInfo) Hash() common.Hash {
|
|||
|
||||
// Quorum Certificate struct in XDPoS 2.0
|
||||
type QuorumCert struct {
|
||||
ProposedBlockInfo *BlockInfo
|
||||
Signatures []Signature
|
||||
GapNumber uint64
|
||||
ProposedBlockInfo *BlockInfo `json:"proposedBlockInfo"`
|
||||
Signatures []Signature `json:"signatures"`
|
||||
GapNumber uint64 `json:"gapNumber"`
|
||||
}
|
||||
|
||||
// Timeout Certificate struct in XDPoS 2.0
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@
|
|||
package vm
|
||||
|
||||
const (
|
||||
set2BitsMask = uint16(0b1100_0000_0000_0000)
|
||||
set3BitsMask = uint16(0b1110_0000_0000_0000)
|
||||
set4BitsMask = uint16(0b1111_0000_0000_0000)
|
||||
set5BitsMask = uint16(0b1111_1000_0000_0000)
|
||||
set6BitsMask = uint16(0b1111_1100_0000_0000)
|
||||
set7BitsMask = uint16(0b1111_1110_0000_0000)
|
||||
set2BitsMask = uint16(0b11)
|
||||
set3BitsMask = uint16(0b111)
|
||||
set4BitsMask = uint16(0b1111)
|
||||
set5BitsMask = uint16(0b1_1111)
|
||||
set6BitsMask = uint16(0b11_1111)
|
||||
set7BitsMask = uint16(0b111_1111)
|
||||
)
|
||||
|
||||
// bitvec is a bit vector which maps bytes in a program.
|
||||
|
|
@ -30,32 +30,26 @@ const (
|
|||
// it's data (i.e. argument of PUSHxx).
|
||||
type bitvec []byte
|
||||
|
||||
var lookup = [8]byte{
|
||||
0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1,
|
||||
}
|
||||
|
||||
func (bits bitvec) set1(pos uint64) {
|
||||
bits[pos/8] |= lookup[pos%8]
|
||||
bits[pos/8] |= 1 << (pos % 8)
|
||||
}
|
||||
|
||||
func (bits bitvec) setN(flag uint16, pos uint64) {
|
||||
a := flag >> (pos % 8)
|
||||
bits[pos/8] |= byte(a >> 8)
|
||||
if b := byte(a); b != 0 {
|
||||
// If the bit-setting affects the neighbouring byte, we can assign - no need to OR it,
|
||||
// since it's the first write to that byte
|
||||
a := flag << (pos % 8)
|
||||
bits[pos/8] |= byte(a)
|
||||
if b := byte(a >> 8); b != 0 {
|
||||
bits[pos/8+1] = b
|
||||
}
|
||||
}
|
||||
|
||||
func (bits bitvec) set8(pos uint64) {
|
||||
a := byte(0xFF >> (pos % 8))
|
||||
a := byte(0xFF << (pos % 8))
|
||||
bits[pos/8] |= a
|
||||
bits[pos/8+1] = ^a
|
||||
}
|
||||
|
||||
func (bits bitvec) set16(pos uint64) {
|
||||
a := byte(0xFF >> (pos % 8))
|
||||
a := byte(0xFF << (pos % 8))
|
||||
bits[pos/8] |= a
|
||||
bits[pos/8+1] = 0xFF
|
||||
bits[pos/8+2] = ^a
|
||||
|
|
@ -63,7 +57,7 @@ func (bits bitvec) set16(pos uint64) {
|
|||
|
||||
// codeSegment checks if the position is in a code segment.
|
||||
func (bits *bitvec) codeSegment(pos uint64) bool {
|
||||
return ((*bits)[pos/8] & (0x80 >> (pos % 8))) == 0
|
||||
return (((*bits)[pos/8] >> (pos % 8)) & 1) == 0
|
||||
}
|
||||
|
||||
// codeBitmap collects data locations in code.
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
|
|
@ -28,24 +29,27 @@ func TestJumpDestAnalysis(t *testing.T) {
|
|||
exp byte
|
||||
which int
|
||||
}{
|
||||
{[]byte{byte(PUSH1), 0x01, 0x01, 0x01}, 0x40, 0},
|
||||
{[]byte{byte(PUSH1), byte(PUSH1), byte(PUSH1), byte(PUSH1)}, 0x50, 0},
|
||||
{[]byte{byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), 0x01, 0x01, 0x01}, 0x7F, 0},
|
||||
{[]byte{byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x80, 1},
|
||||
{[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), byte(PUSH2), byte(PUSH2), 0x01, 0x01, 0x01}, 0x03, 0},
|
||||
{[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), 0x01, 0x01, 0x01, 0x01, 0x01}, 0x00, 1},
|
||||
{[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x74, 0},
|
||||
{[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x00, 1},
|
||||
{[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x3F, 0},
|
||||
{[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0xC0, 1},
|
||||
{[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x7F, 0},
|
||||
{[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0xFF, 1},
|
||||
{[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x80, 2},
|
||||
{[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0x7f, 0},
|
||||
{[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0xA0, 1},
|
||||
{[]byte{byte(PUSH32)}, 0x7F, 0},
|
||||
{[]byte{byte(PUSH32)}, 0xFF, 1},
|
||||
{[]byte{byte(PUSH32)}, 0xFF, 2},
|
||||
{[]byte{byte(PUSH1), 0x01, 0x01, 0x01}, 0b0000_0010, 0},
|
||||
{[]byte{byte(PUSH1), byte(PUSH1), byte(PUSH1), byte(PUSH1)}, 0b0000_1010, 0},
|
||||
{[]byte{0x00, byte(PUSH1), 0x00, byte(PUSH1), 0x00, byte(PUSH1), 0x00, byte(PUSH1)}, 0b0101_0100, 0},
|
||||
{[]byte{byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), 0x01, 0x01, 0x01}, bits.Reverse8(0x7F), 0},
|
||||
{[]byte{byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0001, 1},
|
||||
{[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), byte(PUSH2), byte(PUSH2), 0x01, 0x01, 0x01}, 0b1100_0000, 0},
|
||||
{[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0000, 1},
|
||||
{[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0010_1110, 0},
|
||||
{[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0000, 1},
|
||||
{[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b1111_1100, 0},
|
||||
{[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0011, 1},
|
||||
{[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b1111_1110, 0},
|
||||
{[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b1111_1111, 1},
|
||||
{[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0001, 2},
|
||||
{[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0b1111_1110, 0},
|
||||
{[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0b0000_0101, 1},
|
||||
{[]byte{byte(PUSH32)}, 0b1111_1110, 0},
|
||||
{[]byte{byte(PUSH32)}, 0b1111_1111, 1},
|
||||
{[]byte{byte(PUSH32)}, 0b1111_1111, 2},
|
||||
{[]byte{byte(PUSH32)}, 0b1111_1111, 3},
|
||||
{[]byte{byte(PUSH32)}, 0b0000_0001, 4},
|
||||
}
|
||||
for i, test := range tests {
|
||||
ret := codeBitmap(test.code)
|
||||
|
|
@ -55,9 +59,12 @@ func TestJumpDestAnalysis(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
const analysisCodeSize = 1200 * 1024
|
||||
|
||||
func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) {
|
||||
// 1.4 ms
|
||||
code := make([]byte, 1200000)
|
||||
code := make([]byte, analysisCodeSize)
|
||||
bench.SetBytes(analysisCodeSize)
|
||||
bench.ResetTimer()
|
||||
for i := 0; i < bench.N; i++ {
|
||||
codeBitmap(code)
|
||||
|
|
@ -66,7 +73,8 @@ func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) {
|
|||
}
|
||||
func BenchmarkJumpdestHashing_1200k(bench *testing.B) {
|
||||
// 4 ms
|
||||
code := make([]byte, 1200000)
|
||||
code := make([]byte, analysisCodeSize)
|
||||
bench.SetBytes(analysisCodeSize)
|
||||
bench.ResetTimer()
|
||||
for i := 0; i < bench.N; i++ {
|
||||
crypto.Keccak256Hash(code)
|
||||
|
|
@ -77,13 +85,19 @@ func BenchmarkJumpdestHashing_1200k(bench *testing.B) {
|
|||
func BenchmarkJumpdestOpAnalysis(bench *testing.B) {
|
||||
var op OpCode
|
||||
bencher := func(b *testing.B) {
|
||||
code := make([]byte, 32*b.N)
|
||||
code := make([]byte, analysisCodeSize)
|
||||
b.SetBytes(analysisCodeSize)
|
||||
for i := range code {
|
||||
code[i] = byte(op)
|
||||
}
|
||||
bits := make(bitvec, len(code)/8+1+4)
|
||||
b.ResetTimer()
|
||||
codeBitmapInternal(code, bits)
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := range bits {
|
||||
bits[j] = 0
|
||||
}
|
||||
codeBitmapInternal(code, bits)
|
||||
}
|
||||
}
|
||||
for op = PUSH1; op <= PUSH32; op++ {
|
||||
bench.Run(op.String(), bencher)
|
||||
|
|
|
|||
|
|
@ -17,15 +17,14 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// calcMemSize64 calculates the required memory size, and returns
|
||||
// the size and whether the result overflowed uint64
|
||||
func calcMemSize64(off, l *big.Int) (uint64, bool) {
|
||||
func calcMemSize64(off, l *uint256.Int) (uint64, bool) {
|
||||
if !l.IsUint64() {
|
||||
return 0, true
|
||||
}
|
||||
|
|
@ -35,16 +34,16 @@ func calcMemSize64(off, l *big.Int) (uint64, bool) {
|
|||
// calcMemSize64WithUint calculates the required memory size, and returns
|
||||
// the size and whether the result overflowed uint64
|
||||
// Identical to calcMemSize64, but length is a uint64
|
||||
func calcMemSize64WithUint(off *big.Int, length64 uint64) (uint64, bool) {
|
||||
func calcMemSize64WithUint(off *uint256.Int, length64 uint64) (uint64, bool) {
|
||||
// if length is zero, memsize is always zero, regardless of offset
|
||||
if length64 == 0 {
|
||||
return 0, false
|
||||
}
|
||||
// Check that offset doesn't overflow
|
||||
if !off.IsUint64() {
|
||||
offset64, overflow := off.Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, true
|
||||
}
|
||||
offset64 := off.Uint64()
|
||||
val := offset64 + length64
|
||||
// if value < either of it's parts, then it overflowed
|
||||
return val, val < offset64
|
||||
|
|
@ -64,22 +63,6 @@ func getData(data []byte, start uint64, size uint64) []byte {
|
|||
return common.RightPadBytes(data[start:end], int(size))
|
||||
}
|
||||
|
||||
// getDataBig returns a slice from the data based on the start and size and pads
|
||||
// up to size with zero's. This function is overflow safe.
|
||||
func getDataBig(data []byte, start *big.Int, size *big.Int) []byte {
|
||||
dlen := big.NewInt(int64(len(data)))
|
||||
|
||||
s := math.BigMin(start, dlen)
|
||||
e := math.BigMin(new(big.Int).Add(s, size), dlen)
|
||||
return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64()))
|
||||
}
|
||||
|
||||
// bigUint64 returns the integer casted to a uint64 and returns whether it
|
||||
// overflowed in the process.
|
||||
func bigUint64(v *big.Int) (uint64, bool) {
|
||||
return v.Uint64(), !v.IsUint64()
|
||||
}
|
||||
|
||||
// toWordSize returns the ceiled word size required for memory expansion.
|
||||
func toWordSize(size uint64) uint64 {
|
||||
if size > math.MaxUint64-31 {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// ContractRef is a reference to the contract's backing object
|
||||
|
|
@ -81,11 +82,11 @@ func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uin
|
|||
return c
|
||||
}
|
||||
|
||||
func (c *Contract) validJumpdest(dest *big.Int) bool {
|
||||
udest := dest.Uint64()
|
||||
func (c *Contract) validJumpdest(dest *uint256.Int) bool {
|
||||
udest, overflow := dest.Uint64WithOverflow()
|
||||
// PC cannot go beyond len(code) and certainly can't be bigger than 63bits.
|
||||
// Don't bother checking for JUMPDEST in that case.
|
||||
if dest.BitLen() >= 63 || udest >= uint64(len(c.Code)) {
|
||||
if overflow || udest >= uint64(len(c.Code)) {
|
||||
return false
|
||||
}
|
||||
// Only JUMPDESTs allowed for destinations
|
||||
|
|
@ -131,16 +132,11 @@ func (c *Contract) AsDelegate() *Contract {
|
|||
|
||||
// GetOp returns the n'th element in the contract's byte array
|
||||
func (c *Contract) GetOp(n uint64) OpCode {
|
||||
return OpCode(c.GetByte(n))
|
||||
}
|
||||
|
||||
// GetByte returns the n'th byte in the contract's byte array
|
||||
func (c *Contract) GetByte(n uint64) byte {
|
||||
if n < uint64(len(c.Code)) {
|
||||
return c.Code[n]
|
||||
return OpCode(c.Code[n])
|
||||
}
|
||||
|
||||
return 0
|
||||
return STOP
|
||||
}
|
||||
|
||||
// Caller returns the caller of the contract.
|
||||
|
|
|
|||
|
|
@ -488,6 +488,7 @@ func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) {
|
|||
return runBn256Pairing(input)
|
||||
}
|
||||
|
||||
|
||||
type blake2F struct{}
|
||||
|
||||
func (c *blake2F) RequiredGas(input []byte) uint64 {
|
||||
|
|
@ -521,7 +522,7 @@ func (c *blake2F) Run(input []byte) ([]byte, error) {
|
|||
// Parse the input into the Blake2b call parameters
|
||||
var (
|
||||
rounds = binary.BigEndian.Uint32(input[0:4])
|
||||
final = (input[212] == blake2FFinalBlockBytes)
|
||||
final = input[212] == blake2FFinalBlockBytes
|
||||
|
||||
h [8]uint64
|
||||
m [16]uint64
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ package vm
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// EnableEIP enables the given EIP on the config.
|
||||
|
|
@ -27,6 +29,10 @@ import (
|
|||
// defined jump tables are not polluted.
|
||||
func EnableEIP(eipNum int, jt *JumpTable) error {
|
||||
switch eipNum {
|
||||
case 3855:
|
||||
enable3855(jt)
|
||||
case 3198:
|
||||
enable3198(jt)
|
||||
case 2200:
|
||||
enable2200(jt)
|
||||
case 1884:
|
||||
|
|
@ -51,17 +57,16 @@ func enable1884(jt *JumpTable) {
|
|||
jt[EXTCODEHASH].constantGas = params.ExtcodeHashGasEIP1884
|
||||
|
||||
// New opcode
|
||||
jt[SELFBALANCE] = operation{
|
||||
jt[SELFBALANCE] = &operation{
|
||||
execute: opSelfBalance,
|
||||
constantGas: GasFastStep,
|
||||
minStack: minStack(0, 1),
|
||||
maxStack: maxStack(0, 1),
|
||||
valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
balance := interpreter.intPool.get().Set(interpreter.evm.StateDB.GetBalance(callContext.contract.Address()))
|
||||
balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(callContext.contract.Address()))
|
||||
callContext.stack.push(balance)
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -70,18 +75,17 @@ func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
|
|||
// - Adds an opcode that returns the current chain’s EIP-155 unique identifier
|
||||
func enable1344(jt *JumpTable) {
|
||||
// New opcode
|
||||
jt[CHAINID] = operation{
|
||||
jt[CHAINID] = &operation{
|
||||
execute: opChainID,
|
||||
constantGas: GasQuickStep,
|
||||
minStack: minStack(0, 1),
|
||||
maxStack: maxStack(0, 1),
|
||||
valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
// opChainID implements CHAINID opcode
|
||||
func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
chainId := interpreter.intPool.get().Set(interpreter.evm.chainConfig.ChainId)
|
||||
chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainId)
|
||||
callContext.stack.push(chainId)
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -91,3 +95,39 @@ func enable2200(jt *JumpTable) {
|
|||
jt[SLOAD].constantGas = params.SloadGasEIP2200
|
||||
jt[SSTORE].dynamicGas = gasSStoreEIP2200
|
||||
}
|
||||
|
||||
// enable3198 applies EIP-3198 (BASEFEE Opcode)
|
||||
// - Adds an opcode that returns the current block's base fee.
|
||||
func enable3198(jt *JumpTable) {
|
||||
// New opcode
|
||||
jt[BASEFEE] = &operation{
|
||||
execute: opBaseFee,
|
||||
constantGas: GasQuickStep,
|
||||
minStack: minStack(0, 1),
|
||||
maxStack: maxStack(0, 1),
|
||||
}
|
||||
}
|
||||
|
||||
// opBaseFee implements BASEFEE opcode
|
||||
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
baseFee, _ := uint256.FromBig(common.MinGasPrice50x)
|
||||
callContext.stack.push(baseFee)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// enable3855 applies EIP-3855 (PUSH0 opcode)
|
||||
func enable3855(jt *JumpTable) {
|
||||
// New opcode
|
||||
jt[PUSH0] = &operation{
|
||||
execute: opPush0,
|
||||
constantGas: GasQuickStep,
|
||||
minStack: minStack(0, 1),
|
||||
maxStack: maxStack(0, 1),
|
||||
}
|
||||
}
|
||||
|
||||
// opPush0 implements the PUSH0 opcode
|
||||
func opPush0(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int))
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ var (
|
|||
ErrWriteProtection = errors.New("write protection")
|
||||
ErrReturnDataOutOfBounds = errors.New("return data out of bounds")
|
||||
ErrGasUintOverflow = errors.New("gas uint64 overflow")
|
||||
|
||||
// errStopToken is an internal token indicating interpreter loop termination,
|
||||
// never returned to outside callers.
|
||||
errStopToken = errors.New("stop token")
|
||||
)
|
||||
|
||||
// ErrStackUnderflow wraps an evm error when the items on the stack less
|
||||
|
|
|
|||
|
|
@ -46,12 +46,14 @@ type (
|
|||
// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
|
||||
func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) {
|
||||
if contract.CodeAddr != nil {
|
||||
precompiles := PrecompiledContractsHomestead
|
||||
if evm.chainRules.IsByzantium {
|
||||
precompiles = PrecompiledContractsByzantium
|
||||
}
|
||||
if evm.chainRules.IsIstanbul {
|
||||
var precompiles map[common.Address]PrecompiledContract
|
||||
switch {
|
||||
case evm.chainRules.IsIstanbul:
|
||||
precompiles = PrecompiledContractsIstanbul
|
||||
case evm.chainRules.IsByzantium:
|
||||
precompiles = PrecompiledContractsByzantium
|
||||
default:
|
||||
precompiles = PrecompiledContractsHomestead
|
||||
}
|
||||
if p := precompiles[*contract.CodeAddr]; p != nil {
|
||||
switch p.(type) {
|
||||
|
|
@ -105,6 +107,7 @@ type Context struct {
|
|||
BlockNumber *big.Int // Provides information for NUMBER
|
||||
Time *big.Int // Provides information for TIME
|
||||
Difficulty *big.Int // Provides information for DIFFICULTY
|
||||
Random *common.Hash // Provides information for PREVRANDAO
|
||||
}
|
||||
|
||||
// EVM is the Ethereum Virtual Machine base object and provides
|
||||
|
|
@ -189,9 +192,6 @@ func (evm *EVM) Interpreter() Interpreter {
|
|||
// the necessary steps to create accounts and reverses the state in case of an
|
||||
// execution error or failed value transfer.
|
||||
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
|
||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||
return nil, gas, nil
|
||||
}
|
||||
// Fail if we're trying to execute above the call depth limit
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, gas, ErrDepth
|
||||
|
|
@ -263,9 +263,6 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
|||
// CallCode differs from Call in the sense that it executes the given address'
|
||||
// code with the caller as context.
|
||||
func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
|
||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||
return nil, gas, nil
|
||||
}
|
||||
// Fail if we're trying to execute above the call depth limit
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, gas, ErrDepth
|
||||
|
|
@ -302,9 +299,6 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
|||
// DelegateCall differs from CallCode in the sense that it executes the given address'
|
||||
// code with the caller as context and the caller is set to the caller of the caller.
|
||||
func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
|
||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||
return nil, gas, nil
|
||||
}
|
||||
// Fail if we're trying to execute above the call depth limit
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, gas, ErrDepth
|
||||
|
|
@ -332,9 +326,6 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
|||
// Opcodes that attempt to perform such modifications will result in exceptions
|
||||
// instead of performing the modifications.
|
||||
func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
|
||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||
return nil, gas, nil
|
||||
}
|
||||
// Fail if we're trying to execute above the call depth limit
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, gas, ErrDepth
|
||||
|
|
@ -353,7 +344,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
|||
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
|
||||
// but is the correct thing to do and matters on other networks, in tests, and potential
|
||||
// future scenarios
|
||||
evm.StateDB.AddBalance(addr, bigZero)
|
||||
evm.StateDB.AddBalance(addr, big.NewInt(0))
|
||||
}
|
||||
|
||||
// When an error was returned by the EVM or when setting the creation code
|
||||
|
|
@ -412,10 +403,6 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||
contract := NewContract(caller, AccountRef(address), value, gas)
|
||||
contract.SetCodeOptionalHash(&address, codeAndHash)
|
||||
|
||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||
return nil, address, gas, nil
|
||||
}
|
||||
|
||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value)
|
||||
}
|
||||
|
|
@ -423,13 +410,16 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||
|
||||
ret, err := run(evm, contract, nil, false)
|
||||
|
||||
// check whether the max code size has been exceeded
|
||||
maxCodeSizeExceeded := evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize
|
||||
// Check whether the max code size has been exceeded, assign err if the case.
|
||||
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
|
||||
err = ErrMaxCodeSizeExceeded
|
||||
}
|
||||
|
||||
// if the contract creation ran successfully and no errors were returned
|
||||
// calculate the gas required to store the code. If the code could not
|
||||
// be stored due to not enough gas set an error and let it be handled
|
||||
// by the error checking condition below.
|
||||
if err == nil && !maxCodeSizeExceeded {
|
||||
if err == nil {
|
||||
createDataGas := uint64(len(ret)) * params.CreateDataGas
|
||||
if contract.UseGas(createDataGas) {
|
||||
evm.StateDB.SetCode(address, ret)
|
||||
|
|
@ -441,21 +431,17 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||
// When an error was returned by the EVM or when setting the creation code
|
||||
// above we revert to the snapshot and consume any gas remaining. Additionally
|
||||
// when we're in homestead this also counts for code storage gas errors.
|
||||
if maxCodeSizeExceeded || (err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas)) {
|
||||
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != ErrExecutionReverted {
|
||||
contract.UseGas(contract.Gas)
|
||||
}
|
||||
}
|
||||
// Assign err if contract code size exceeds the max while the err is still empty.
|
||||
if maxCodeSizeExceeded && err == nil {
|
||||
err = ErrMaxCodeSizeExceeded
|
||||
}
|
||||
|
||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
||||
}
|
||||
return ret, address, contract.Gas, err
|
||||
|
||||
}
|
||||
|
||||
// Create creates a new contract using code as deployment code.
|
||||
|
|
@ -466,7 +452,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
|
|||
|
||||
// Create2 creates a new contract using code as deployment code.
|
||||
//
|
||||
// The different between Create2 with Create is Create2 uses sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code))[12:]
|
||||
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
|
||||
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
|
||||
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||
codeAndHash := &codeAndHash{code: code}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// Gas costs
|
||||
|
|
@ -34,7 +34,7 @@ const (
|
|||
//
|
||||
// The cost of gas was changed during the homestead price change HF.
|
||||
// As part of EIP 150 (TangerineWhistle), the returned gas is gas - base * 63 / 64.
|
||||
func callGas(isEip150 bool, availableGas, base uint64, callCost *big.Int) (uint64, error) {
|
||||
func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (uint64, error) {
|
||||
if isEip150 {
|
||||
availableGas = availableGas - base
|
||||
gas := availableGas - availableGas/64
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
|
|||
// as argument:
|
||||
// CALLDATACOPY (stack position 2)
|
||||
// CODECOPY (stack position 2)
|
||||
// EXTCODECOPY (stack poition 3)
|
||||
// EXTCODECOPY (stack position 3)
|
||||
// RETURNDATACOPY (stack position 2)
|
||||
func memoryCopierGas(stackpos int) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
|
|
@ -71,7 +71,7 @@ func memoryCopierGas(stackpos int) gasFunc {
|
|||
return 0, err
|
||||
}
|
||||
// And gas for copying data, charged per word at param.CopyGas
|
||||
words, overflow := bigUint64(stack.Back(stackpos))
|
||||
words, overflow := stack.Back(stackpos).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
|
|
@ -97,7 +97,7 @@ var (
|
|||
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var (
|
||||
y, x = stack.Back(1), stack.Back(0)
|
||||
current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
|
||||
current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32()))
|
||||
)
|
||||
// The legacy gas metering only takes into consideration the current state
|
||||
// Legacy rules should be applied if we are in Petersburg (removal of EIP-1283)
|
||||
|
|
@ -132,11 +132,11 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
|||
// 2.2.2. If original value equals new value (this storage slot is reset)
|
||||
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
|
||||
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
|
||||
value := common.BigToHash(y)
|
||||
value := common.Hash(y.Bytes32())
|
||||
if current == value { // noop (1)
|
||||
return params.NetSstoreNoopGas, nil
|
||||
}
|
||||
original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
|
||||
original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
|
||||
if original == current {
|
||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||
return params.NetSstoreInitGas, nil
|
||||
|
|
@ -164,18 +164,18 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
|||
}
|
||||
|
||||
// 0. If *gasleft* is less than or equal to 2300, fail the current call.
|
||||
// 1. If current value equals new value (this is a no-op), SSTORE_NOOP_GAS gas is deducted.
|
||||
// 1. If current value equals new value (this is a no-op), SLOAD_GAS is deducted.
|
||||
// 2. If current value does not equal new value:
|
||||
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context):
|
||||
// 2.1.1. If original value is 0, SSTORE_INIT_GAS gas is deducted.
|
||||
// 2.1.2. Otherwise, SSTORE_CLEAN_GAS gas is deducted. If new value is 0, add SSTORE_CLEAR_REFUND to refund counter.
|
||||
// 2.2. If original value does not equal current value (this storage slot is dirty), SSTORE_DIRTY_GAS gas is deducted. Apply both of the following clauses:
|
||||
// 2.1.1. If original value is 0, SSTORE_SET_GAS (20K) gas is deducted.
|
||||
// 2.1.2. Otherwise, SSTORE_RESET_GAS gas is deducted. If new value is 0, add SSTORE_CLEARS_SCHEDULE to refund counter.
|
||||
// 2.2. If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses:
|
||||
// 2.2.1. If original value is not 0:
|
||||
// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEAR_REFUND gas from refund counter. We can prove that refund counter will never go below 0.
|
||||
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEAR_REFUND gas to refund counter.
|
||||
// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter.
|
||||
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter.
|
||||
// 2.2.2. If original value equals new value (this storage slot is reset):
|
||||
// 2.2.2.1. If original value is 0, add SSTORE_INIT_REFUND to refund counter.
|
||||
// 2.2.2.2. Otherwise, add SSTORE_CLEAN_REFUND gas to refund counter.
|
||||
// 2.2.2.1. If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter.
|
||||
// 2.2.2.2. Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter.
|
||||
func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
// If we fail the minimum gas availability invariant, fail (0)
|
||||
if contract.Gas <= params.SstoreSentryGasEIP2200 {
|
||||
|
|
@ -184,43 +184,43 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
|||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||
var (
|
||||
y, x = stack.Back(1), stack.Back(0)
|
||||
current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
|
||||
current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32()))
|
||||
)
|
||||
value := common.BigToHash(y)
|
||||
value := common.Hash(y.Bytes32())
|
||||
|
||||
if current == value { // noop (1)
|
||||
return params.SstoreNoopGasEIP2200, nil
|
||||
return params.SloadGasEIP2200, nil
|
||||
}
|
||||
original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
|
||||
original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
|
||||
if original == current {
|
||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||
return params.SstoreInitGasEIP2200, nil
|
||||
return params.SstoreSetGasEIP2200, nil
|
||||
}
|
||||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||
evm.StateDB.AddRefund(params.SstoreClearRefundEIP2200)
|
||||
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
|
||||
}
|
||||
return params.SstoreCleanGasEIP2200, nil // write existing slot (2.1.2)
|
||||
return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
|
||||
}
|
||||
if original != (common.Hash{}) {
|
||||
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
|
||||
evm.StateDB.SubRefund(params.SstoreClearRefundEIP2200)
|
||||
evm.StateDB.SubRefund(params.SstoreClearsScheduleRefundEIP2200)
|
||||
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
|
||||
evm.StateDB.AddRefund(params.SstoreClearRefundEIP2200)
|
||||
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
|
||||
}
|
||||
}
|
||||
if original == value {
|
||||
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
|
||||
evm.StateDB.AddRefund(params.SstoreInitRefundEIP2200)
|
||||
evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
|
||||
} else { // reset to original existing slot (2.2.2.2)
|
||||
evm.StateDB.AddRefund(params.SstoreCleanRefundEIP2200)
|
||||
evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
|
||||
}
|
||||
}
|
||||
return params.SstoreDirtyGasEIP2200, nil // dirty update (2.2)
|
||||
return params.SloadGasEIP2200, nil // dirty update (2.2)
|
||||
}
|
||||
|
||||
func makeGasLog(n uint64) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
requestedSize, overflow := bigUint64(stack.Back(1))
|
||||
requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
|
|
@ -248,16 +248,16 @@ func makeGasLog(n uint64) gasFunc {
|
|||
}
|
||||
}
|
||||
|
||||
func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
wordGas, overflow := bigUint64(stack.Back(1))
|
||||
wordGas, overflow := stack.Back(1).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow {
|
||||
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
||||
|
|
@ -287,11 +287,11 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
|
|||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
wordGas, overflow := bigUint64(stack.Back(2))
|
||||
wordGas, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow {
|
||||
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
||||
|
|
@ -329,8 +329,8 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
|
|||
func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var (
|
||||
gas uint64
|
||||
transfersValue = stack.Back(2).Sign() != 0
|
||||
address = common.BigToAddress(stack.Back(1))
|
||||
transfersValue = !stack.Back(2).IsZero()
|
||||
address = common.Address(stack.Back(1).Bytes20())
|
||||
)
|
||||
if evm.chainRules.IsEIP158 {
|
||||
if transfersValue && evm.StateDB.Empty(address) {
|
||||
|
|
@ -423,7 +423,7 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
// EIP150 homestead gas reprice fork:
|
||||
if evm.chainRules.IsEIP150 {
|
||||
gas = params.SelfdestructGasEIP150
|
||||
var address = common.BigToAddress(stack.Back(0))
|
||||
var address = common.Address(stack.Back(0).Bytes20())
|
||||
|
||||
if evm.chainRules.IsEIP158 {
|
||||
// if empty and transfers value
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -25,10 +25,10 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
type TwoOperandTestcase struct {
|
||||
|
|
@ -42,6 +42,7 @@ type twoOperandParams struct {
|
|||
y string
|
||||
}
|
||||
|
||||
var alphabetSoup = "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
var commonParams []*twoOperandParams
|
||||
var twoOpMethods map[string]executionFunc
|
||||
|
||||
|
|
@ -91,31 +92,6 @@ func init() {
|
|||
}
|
||||
}
|
||||
|
||||
// getResult is a convenience function to generate the expected values
|
||||
func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
|
||||
var (
|
||||
env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
pc = uint64(0)
|
||||
interpreter = env.interpreter.(*EVMInterpreter)
|
||||
)
|
||||
interpreter.intPool = poolOfIntPools.get()
|
||||
result := make([]TwoOperandTestcase, len(args))
|
||||
for i, param := range args {
|
||||
x := new(big.Int).SetBytes(common.Hex2Bytes(param.x))
|
||||
y := new(big.Int).SetBytes(common.Hex2Bytes(param.y))
|
||||
stack.push(x)
|
||||
stack.push(y)
|
||||
_, err := opFn(&pc, interpreter, &callCtx{nil, stack, nil})
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
actual := stack.pop()
|
||||
result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) {
|
||||
|
||||
var (
|
||||
|
|
@ -124,42 +100,23 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
|
|||
pc = uint64(0)
|
||||
evmInterpreter = env.interpreter.(*EVMInterpreter)
|
||||
)
|
||||
// Stuff a couple of nonzero bigints into pool, to ensure that ops do not rely on pooled integers to be zero
|
||||
evmInterpreter.intPool = poolOfIntPools.get()
|
||||
evmInterpreter.intPool.put(big.NewInt(-1337))
|
||||
evmInterpreter.intPool.put(big.NewInt(-1337))
|
||||
evmInterpreter.intPool.put(big.NewInt(-1337))
|
||||
|
||||
for i, test := range tests {
|
||||
x := new(big.Int).SetBytes(common.Hex2Bytes(test.X))
|
||||
y := new(big.Int).SetBytes(common.Hex2Bytes(test.Y))
|
||||
expected := new(big.Int).SetBytes(common.Hex2Bytes(test.Expected))
|
||||
x := new(uint256.Int).SetBytes(common.Hex2Bytes(test.X))
|
||||
y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Y))
|
||||
expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected))
|
||||
stack.push(x)
|
||||
stack.push(y)
|
||||
opFn(&pc, evmInterpreter, &callCtx{nil, stack, nil})
|
||||
if len(stack.data) != 1 {
|
||||
t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data))
|
||||
}
|
||||
actual := stack.pop()
|
||||
|
||||
if actual.Cmp(expected) != 0 {
|
||||
t.Errorf("Testcase %v %d, %v(%x, %x): expected %x, got %x", name, i, name, x, y, expected, actual)
|
||||
}
|
||||
// Check pool usage
|
||||
// 1.pool is not allowed to contain anything on the stack
|
||||
// 2.pool is not allowed to contain the same pointers twice
|
||||
if evmInterpreter.intPool.pool.len() > 0 {
|
||||
|
||||
poolvals := make(map[*big.Int]struct{})
|
||||
poolvals[actual] = struct{}{}
|
||||
|
||||
for evmInterpreter.intPool.pool.len() > 0 {
|
||||
key := evmInterpreter.intPool.get()
|
||||
if _, exist := poolvals[key]; exist {
|
||||
t.Errorf("Testcase %v %d, pool contains double-entry", name, i)
|
||||
}
|
||||
poolvals[key] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
poolOfIntPools.put(evmInterpreter.intPool)
|
||||
}
|
||||
|
||||
func TestByteOp(t *testing.T) {
|
||||
|
|
@ -235,6 +192,68 @@ func TestSAR(t *testing.T) {
|
|||
testTwoOperandOp(t, tests, opSAR, "sar")
|
||||
}
|
||||
|
||||
func TestAddMod(t *testing.T) {
|
||||
var (
|
||||
env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
||||
pc = uint64(0)
|
||||
)
|
||||
tests := []struct {
|
||||
x string
|
||||
y string
|
||||
z string
|
||||
expected string
|
||||
}{
|
||||
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
|
||||
},
|
||||
}
|
||||
// x + y = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd
|
||||
// in 256 bit repr, fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd
|
||||
|
||||
for i, test := range tests {
|
||||
x := new(uint256.Int).SetBytes(common.Hex2Bytes(test.x))
|
||||
y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.y))
|
||||
z := new(uint256.Int).SetBytes(common.Hex2Bytes(test.z))
|
||||
expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.expected))
|
||||
stack.push(z)
|
||||
stack.push(y)
|
||||
stack.push(x)
|
||||
opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil})
|
||||
actual := stack.pop()
|
||||
if actual.Cmp(expected) != 0 {
|
||||
t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getResult is a convenience function to generate the expected values
|
||||
func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
|
||||
var (
|
||||
env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
pc = uint64(0)
|
||||
interpreter = env.interpreter.(*EVMInterpreter)
|
||||
)
|
||||
result := make([]TwoOperandTestcase, len(args))
|
||||
for i, param := range args {
|
||||
x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x))
|
||||
y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
|
||||
stack.push(x)
|
||||
stack.push(y)
|
||||
_, err := opFn(&pc, interpreter, &callCtx{nil, stack, nil})
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
actual := stack.pop()
|
||||
result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// utility function to fill the json-file with testcases
|
||||
// Enable this test to generate the 'testcases_xx.json' files
|
||||
func TestWriteExpectedValues(t *testing.T) {
|
||||
|
|
@ -276,7 +295,6 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
|
|||
)
|
||||
|
||||
env.interpreter = evmInterpreter
|
||||
evmInterpreter.intPool = poolOfIntPools.get()
|
||||
// convert args
|
||||
byteArgs := make([][]byte, len(args))
|
||||
for i, arg := range args {
|
||||
|
|
@ -286,13 +304,13 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
|
|||
bench.ResetTimer()
|
||||
for i := 0; i < bench.N; i++ {
|
||||
for _, arg := range byteArgs {
|
||||
a := new(big.Int).SetBytes(arg)
|
||||
a := new(uint256.Int)
|
||||
a.SetBytes(arg)
|
||||
stack.push(a)
|
||||
}
|
||||
op(&pc, evmInterpreter, &callCtx{nil, stack, nil})
|
||||
stack.pop()
|
||||
}
|
||||
poolOfIntPools.put(evmInterpreter.intPool)
|
||||
}
|
||||
|
||||
func BenchmarkOpAdd64(b *testing.B) {
|
||||
|
|
@ -338,8 +356,8 @@ func BenchmarkOpSub256(b *testing.B) {
|
|||
}
|
||||
|
||||
func BenchmarkOpMul(b *testing.B) {
|
||||
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
x := alphabetSoup
|
||||
y := alphabetSoup
|
||||
|
||||
opBenchmark(b, opMul, x, y)
|
||||
}
|
||||
|
|
@ -370,64 +388,64 @@ func BenchmarkOpSdiv(b *testing.B) {
|
|||
}
|
||||
|
||||
func BenchmarkOpMod(b *testing.B) {
|
||||
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
x := alphabetSoup
|
||||
y := alphabetSoup
|
||||
|
||||
opBenchmark(b, opMod, x, y)
|
||||
}
|
||||
|
||||
func BenchmarkOpSmod(b *testing.B) {
|
||||
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
x := alphabetSoup
|
||||
y := alphabetSoup
|
||||
|
||||
opBenchmark(b, opSmod, x, y)
|
||||
}
|
||||
|
||||
func BenchmarkOpExp(b *testing.B) {
|
||||
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
x := alphabetSoup
|
||||
y := alphabetSoup
|
||||
|
||||
opBenchmark(b, opExp, x, y)
|
||||
}
|
||||
|
||||
func BenchmarkOpSignExtend(b *testing.B) {
|
||||
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
x := alphabetSoup
|
||||
y := alphabetSoup
|
||||
|
||||
opBenchmark(b, opSignExtend, x, y)
|
||||
}
|
||||
|
||||
func BenchmarkOpLt(b *testing.B) {
|
||||
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
x := alphabetSoup
|
||||
y := alphabetSoup
|
||||
|
||||
opBenchmark(b, opLt, x, y)
|
||||
}
|
||||
|
||||
func BenchmarkOpGt(b *testing.B) {
|
||||
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
x := alphabetSoup
|
||||
y := alphabetSoup
|
||||
|
||||
opBenchmark(b, opGt, x, y)
|
||||
}
|
||||
|
||||
func BenchmarkOpSlt(b *testing.B) {
|
||||
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
x := alphabetSoup
|
||||
y := alphabetSoup
|
||||
|
||||
opBenchmark(b, opSlt, x, y)
|
||||
}
|
||||
|
||||
func BenchmarkOpSgt(b *testing.B) {
|
||||
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
x := alphabetSoup
|
||||
y := alphabetSoup
|
||||
|
||||
opBenchmark(b, opSgt, x, y)
|
||||
}
|
||||
|
||||
func BenchmarkOpEq(b *testing.B) {
|
||||
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
x := alphabetSoup
|
||||
y := alphabetSoup
|
||||
|
||||
opBenchmark(b, opEq, x, y)
|
||||
}
|
||||
|
|
@ -437,45 +455,45 @@ func BenchmarkOpEq2(b *testing.B) {
|
|||
opBenchmark(b, opEq, x, y)
|
||||
}
|
||||
func BenchmarkOpAnd(b *testing.B) {
|
||||
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
x := alphabetSoup
|
||||
y := alphabetSoup
|
||||
|
||||
opBenchmark(b, opAnd, x, y)
|
||||
}
|
||||
|
||||
func BenchmarkOpOr(b *testing.B) {
|
||||
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
x := alphabetSoup
|
||||
y := alphabetSoup
|
||||
|
||||
opBenchmark(b, opOr, x, y)
|
||||
}
|
||||
|
||||
func BenchmarkOpXor(b *testing.B) {
|
||||
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
x := alphabetSoup
|
||||
y := alphabetSoup
|
||||
|
||||
opBenchmark(b, opXor, x, y)
|
||||
}
|
||||
|
||||
func BenchmarkOpByte(b *testing.B) {
|
||||
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
x := alphabetSoup
|
||||
y := alphabetSoup
|
||||
|
||||
opBenchmark(b, opByte, x, y)
|
||||
}
|
||||
|
||||
func BenchmarkOpAddmod(b *testing.B) {
|
||||
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
z := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
x := alphabetSoup
|
||||
y := alphabetSoup
|
||||
z := alphabetSoup
|
||||
|
||||
opBenchmark(b, opAddmod, x, y, z)
|
||||
}
|
||||
|
||||
func BenchmarkOpMulmod(b *testing.B) {
|
||||
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
z := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
|
||||
x := alphabetSoup
|
||||
y := alphabetSoup
|
||||
z := alphabetSoup
|
||||
|
||||
opBenchmark(b, opMulmod, x, y, z)
|
||||
}
|
||||
|
|
@ -512,21 +530,21 @@ func TestOpMstore(t *testing.T) {
|
|||
)
|
||||
|
||||
env.interpreter = evmInterpreter
|
||||
evmInterpreter.intPool = poolOfIntPools.get()
|
||||
mem.Resize(64)
|
||||
pc := uint64(0)
|
||||
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
|
||||
stack.pushN(new(big.Int).SetBytes(common.Hex2Bytes(v)), big.NewInt(0))
|
||||
stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v)))
|
||||
stack.push(new(uint256.Int))
|
||||
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
|
||||
if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v {
|
||||
t.Fatalf("Mstore fail, got %v, expected %v", got, v)
|
||||
}
|
||||
stack.pushN(big.NewInt(0x1), big.NewInt(0))
|
||||
stack.push(new(uint256.Int).SetUint64(0x1))
|
||||
stack.push(new(uint256.Int))
|
||||
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
|
||||
if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
|
||||
t.Fatalf("Mstore failed to overwrite previous value")
|
||||
}
|
||||
poolOfIntPools.put(evmInterpreter.intPool)
|
||||
}
|
||||
|
||||
func BenchmarkOpMstore(bench *testing.B) {
|
||||
|
|
@ -538,21 +556,20 @@ func BenchmarkOpMstore(bench *testing.B) {
|
|||
)
|
||||
|
||||
env.interpreter = evmInterpreter
|
||||
evmInterpreter.intPool = poolOfIntPools.get()
|
||||
mem.Resize(64)
|
||||
pc := uint64(0)
|
||||
memStart := big.NewInt(0)
|
||||
value := big.NewInt(0x1337)
|
||||
memStart := new(uint256.Int)
|
||||
value := new(uint256.Int).SetUint64(0x1337)
|
||||
|
||||
bench.ResetTimer()
|
||||
for i := 0; i < bench.N; i++ {
|
||||
stack.pushN(value, memStart)
|
||||
stack.push(value)
|
||||
stack.push(memStart)
|
||||
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
|
||||
}
|
||||
poolOfIntPools.put(evmInterpreter.intPool)
|
||||
}
|
||||
|
||||
func BenchmarkOpSHA3(bench *testing.B) {
|
||||
func BenchmarkOpKeccak256(bench *testing.B) {
|
||||
var (
|
||||
env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
|
|
@ -560,17 +577,16 @@ func BenchmarkOpSHA3(bench *testing.B) {
|
|||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
||||
)
|
||||
env.interpreter = evmInterpreter
|
||||
evmInterpreter.intPool = poolOfIntPools.get()
|
||||
mem.Resize(32)
|
||||
pc := uint64(0)
|
||||
start := big.NewInt(0)
|
||||
start := new(uint256.Int)
|
||||
|
||||
bench.ResetTimer()
|
||||
for i := 0; i < bench.N; i++ {
|
||||
stack.pushN(big.NewInt(32), start)
|
||||
opSha3(&pc, evmInterpreter, &callCtx{mem, stack, nil})
|
||||
stack.push(uint256.NewInt(32))
|
||||
stack.push(start)
|
||||
opKeccak256(&pc, evmInterpreter, &callCtx{mem, stack, nil})
|
||||
}
|
||||
poolOfIntPools.put(evmInterpreter.intPool)
|
||||
}
|
||||
|
||||
func TestCreate2Addreses(t *testing.T) {
|
||||
|
|
@ -644,6 +660,38 @@ func TestCreate2Addreses(t *testing.T) {
|
|||
if !bytes.Equal(expected.Bytes(), address.Bytes()) {
|
||||
t.Errorf("test %d: expected %s, got %s", i, expected.String(), address.String())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandom(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
random common.Hash
|
||||
}
|
||||
|
||||
for _, tt := range []testcase{
|
||||
{name: "empty hash", random: common.Hash{}},
|
||||
{name: "1", random: common.Hash{0}},
|
||||
{name: "emptyCodeHash", random: emptyCodeHash},
|
||||
{name: "hash(0x010203)", random: crypto.Keccak256Hash([]byte{0x01, 0x02, 0x03})},
|
||||
} {
|
||||
var (
|
||||
env = NewEVM(Context{Random: &tt.random}, nil, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
pc = uint64(0)
|
||||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
||||
)
|
||||
opRandom(&pc, evmInterpreter, &callCtx{nil, stack, nil})
|
||||
if len(stack.data) != 1 {
|
||||
t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
|
||||
}
|
||||
actual := stack.pop()
|
||||
expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.random.Bytes()))
|
||||
if overflow {
|
||||
t.Errorf("Testcase %v: invalid overflow", tt.name)
|
||||
}
|
||||
if actual.Cmp(expected) != 0 {
|
||||
t.Errorf("Testcase %v: expected %x, got %x", tt.name, expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright 2017 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/>.
|
||||
|
||||
// +build VERIFY_EVM_INTEGER_POOL
|
||||
|
||||
package vm
|
||||
|
||||
import "fmt"
|
||||
|
||||
const verifyPool = true
|
||||
|
||||
func verifyIntegerPool(ip *intPool) {
|
||||
for i, item := range ip.pool.data {
|
||||
if item.Cmp(checkVal) != 0 {
|
||||
panic(fmt.Sprintf("%d'th item failed aggressive pool check. Value was modified", i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright 2017 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/>.
|
||||
|
||||
// +build !VERIFY_EVM_INTEGER_POOL
|
||||
|
||||
package vm
|
||||
|
||||
const verifyPool = false
|
||||
|
||||
func verifyIntegerPool(ip *intPool) {}
|
||||
|
|
@ -18,7 +18,6 @@ package vm
|
|||
|
||||
import (
|
||||
"hash"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||
|
|
@ -29,10 +28,9 @@ import (
|
|||
type Config struct {
|
||||
Debug bool // Enables debugging
|
||||
Tracer Tracer // Opcode logger
|
||||
NoRecursion bool // Disables call, callcode, delegate call and create
|
||||
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
|
||||
|
||||
JumpTable [256]operation // EVM instruction table, automatically populated if unset
|
||||
JumpTable *JumpTable // EVM instruction table, automatically populated if unset
|
||||
|
||||
EWASMInterpreter string // External EWASM interpreter options
|
||||
EVMInterpreter string // External EVM interpreter options
|
||||
|
|
@ -83,8 +81,6 @@ type EVMInterpreter struct {
|
|||
evm *EVM
|
||||
cfg Config
|
||||
|
||||
intPool *intPool
|
||||
|
||||
hasher keccakState // Keccak256 hasher instance shared across opcodes
|
||||
hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes
|
||||
|
||||
|
|
@ -94,35 +90,44 @@ type EVMInterpreter struct {
|
|||
|
||||
// NewEVMInterpreter returns a new instance of the Interpreter.
|
||||
func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
|
||||
// We use the STOP instruction whether to see
|
||||
// the jump table was initialised. If it was not
|
||||
// we'll set the default jump table.
|
||||
if !cfg.JumpTable[STOP].valid {
|
||||
var jt JumpTable
|
||||
// If jump table was not initialised we set the default one.
|
||||
if cfg.JumpTable == nil {
|
||||
switch {
|
||||
case evm.chainRules.IsShanghai:
|
||||
cfg.JumpTable = &shanghaiInstructionSet
|
||||
case evm.chainRules.IsMerge:
|
||||
cfg.JumpTable = &mergeInstructionSet
|
||||
case evm.chainRules.IsLondon:
|
||||
cfg.JumpTable = &londonInstructionSet
|
||||
case evm.chainRules.IsBerlin:
|
||||
cfg.JumpTable = &berlinInstructionSet
|
||||
case evm.chainRules.IsIstanbul:
|
||||
jt = istanbulInstructionSet
|
||||
cfg.JumpTable = &istanbulInstructionSet
|
||||
case evm.chainRules.IsConstantinople:
|
||||
jt = constantinopleInstructionSet
|
||||
cfg.JumpTable = &constantinopleInstructionSet
|
||||
case evm.chainRules.IsByzantium:
|
||||
jt = byzantiumInstructionSet
|
||||
cfg.JumpTable = &byzantiumInstructionSet
|
||||
case evm.chainRules.IsEIP158:
|
||||
jt = spuriousDragonInstructionSet
|
||||
cfg.JumpTable = &spuriousDragonInstructionSet
|
||||
case evm.chainRules.IsEIP150:
|
||||
jt = tangerineWhistleInstructionSet
|
||||
cfg.JumpTable = &tangerineWhistleInstructionSet
|
||||
case evm.chainRules.IsHomestead:
|
||||
jt = homesteadInstructionSet
|
||||
cfg.JumpTable = &homesteadInstructionSet
|
||||
default:
|
||||
jt = frontierInstructionSet
|
||||
cfg.JumpTable = &frontierInstructionSet
|
||||
}
|
||||
for i, eip := range cfg.ExtraEips {
|
||||
if err := EnableEIP(eip, &jt); err != nil {
|
||||
var extraEips []int
|
||||
for _, eip := range cfg.ExtraEips {
|
||||
copy := *cfg.JumpTable
|
||||
if err := EnableEIP(eip, ©); err != nil {
|
||||
// Disable it, so caller can check if it's activated or not
|
||||
cfg.ExtraEips = append(cfg.ExtraEips[:i], cfg.ExtraEips[i+1:]...)
|
||||
log.Error("EIP activation failed", "eip", eip, "error", err)
|
||||
} else {
|
||||
extraEips = append(extraEips, eip)
|
||||
}
|
||||
cfg.JumpTable = ©
|
||||
}
|
||||
cfg.JumpTable = jt
|
||||
cfg.ExtraEips = extraEips
|
||||
}
|
||||
|
||||
return &EVMInterpreter{
|
||||
|
|
@ -138,20 +143,13 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
|
|||
// considered a revert-and-consume-all-gas operation except for
|
||||
// ErrExecutionReverted which means revert-and-keep-gas-left.
|
||||
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
|
||||
if in.intPool == nil {
|
||||
in.intPool = poolOfIntPools.get()
|
||||
defer func() {
|
||||
poolOfIntPools.put(in.intPool)
|
||||
in.intPool = nil
|
||||
}()
|
||||
}
|
||||
|
||||
// Increment the call depth which is restricted to 1024
|
||||
in.evm.depth++
|
||||
defer func() { in.evm.depth-- }()
|
||||
|
||||
// Make sure the readOnly is only set if we aren't in readOnly yet.
|
||||
// This makes also sure that the readOnly flag isn't removed for child calls.
|
||||
// This also makes sure that the readOnly flag isn't removed for child calls.
|
||||
if readOnly && !in.readOnly {
|
||||
in.readOnly = true
|
||||
defer func() { in.readOnly = false }()
|
||||
|
|
@ -188,9 +186,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
)
|
||||
contract.Input = input
|
||||
|
||||
// Reclaim the stack as an int pool when the execution stops
|
||||
defer func() { in.intPool.put(stack.data...) }()
|
||||
|
||||
if in.cfg.Debug {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
|
|
@ -206,12 +201,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
// explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
|
||||
// the execution of one of the operations or until the done flag is set by the
|
||||
// parent context.
|
||||
steps := 0
|
||||
for {
|
||||
steps++
|
||||
if steps%1000 == 0 && atomic.LoadInt32(&in.evm.abort) != 0 {
|
||||
break
|
||||
}
|
||||
if in.cfg.Debug {
|
||||
// Capture pre-execution values for tracing.
|
||||
logged, pcCopy, gasCopy = false, pc, contract.Gas
|
||||
|
|
@ -221,26 +211,12 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
// enough stack items available to perform the operation.
|
||||
op = contract.GetOp(pc)
|
||||
operation := in.cfg.JumpTable[op]
|
||||
if !operation.valid {
|
||||
return nil, &ErrInvalidOpCode{opcode: op}
|
||||
}
|
||||
// Validate stack
|
||||
if sLen := stack.len(); sLen < operation.minStack {
|
||||
return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
|
||||
} else if sLen > operation.maxStack {
|
||||
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
|
||||
}
|
||||
// If the operation is valid, enforce and write restrictions
|
||||
if in.readOnly && in.evm.chainRules.IsByzantium {
|
||||
// If the interpreter is operating in readonly mode, make sure no
|
||||
// state-modifying operation is performed. The 3rd stack item
|
||||
// for a call operation is the value. Transferring value from one
|
||||
// account to the others means the state is modified and should also
|
||||
// return with an error.
|
||||
if operation.writes || (op == CALL && stack.Back(2).Sign() != 0) {
|
||||
return nil, ErrWriteProtection
|
||||
}
|
||||
}
|
||||
// Static portion of gas
|
||||
cost = operation.constantGas // For tracing
|
||||
if !contract.UseGas(operation.constantGas) {
|
||||
|
|
@ -285,29 +261,17 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
|
||||
// execute the operation
|
||||
res, err = operation.execute(&pc, in, callContext)
|
||||
// verifyPool is a build flag. Pool verification makes sure the integrity
|
||||
// of the integer pool by comparing values to a default value.
|
||||
if verifyPool {
|
||||
verifyIntegerPool(in.intPool)
|
||||
}
|
||||
// if the operation clears the return data (e.g. it has returning data)
|
||||
// set the last return to the result of the operation.
|
||||
if operation.returns {
|
||||
in.returnData = res
|
||||
}
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
case operation.reverts:
|
||||
log.Debug("ErrExecutionReverted", "pc", pc, "reverts", operation.reverts, "err", err)
|
||||
return res, ErrExecutionReverted
|
||||
case operation.halts:
|
||||
return res, nil
|
||||
case !operation.jumps:
|
||||
pc++
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
pc++
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
if err == errStopToken {
|
||||
err = nil // clear stop token error
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
// CanRun tells if the contract, passed as an argument, can be
|
||||
|
|
|
|||
|
|
@ -1,117 +0,0 @@
|
|||
// Copyright 2017 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 vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var checkVal = big.NewInt(-42)
|
||||
|
||||
const poolLimit = 256
|
||||
|
||||
// intPool is a pool of big integers that
|
||||
// can be reused for all big.Int operations.
|
||||
type intPool struct {
|
||||
pool *Stack
|
||||
}
|
||||
|
||||
func newIntPool() *intPool {
|
||||
return &intPool{pool: newstack()}
|
||||
}
|
||||
|
||||
// get retrieves a big int from the pool, allocating one if the pool is empty.
|
||||
// Note, the returned int's value is arbitrary and will not be zeroed!
|
||||
func (p *intPool) get() *big.Int {
|
||||
if p.pool.len() > 0 {
|
||||
return p.pool.pop()
|
||||
}
|
||||
return new(big.Int)
|
||||
}
|
||||
|
||||
// getZero retrieves a big int from the pool, setting it to zero or allocating
|
||||
// a new one if the pool is empty.
|
||||
func (p *intPool) getZero() *big.Int {
|
||||
if p.pool.len() > 0 {
|
||||
return p.pool.pop().SetUint64(0)
|
||||
}
|
||||
return new(big.Int)
|
||||
}
|
||||
|
||||
// putOne returns an allocated big int to the pool to be later reused by get calls.
|
||||
// Note, the values as saved as is; neither put nor get zeroes the ints out!
|
||||
// As opposed to 'put' with variadic args, this method becomes inlined by the
|
||||
// go compiler
|
||||
func (p *intPool) putOne(i *big.Int) {
|
||||
if len(p.pool.data) > poolLimit {
|
||||
return
|
||||
}
|
||||
p.pool.push(i)
|
||||
}
|
||||
|
||||
// put returns an allocated big int to the pool to be later reused by get calls.
|
||||
// Note, the values as saved as is; neither put nor get zeroes the ints out!
|
||||
func (p *intPool) put(is ...*big.Int) {
|
||||
if len(p.pool.data) > poolLimit {
|
||||
return
|
||||
}
|
||||
for _, i := range is {
|
||||
// verifyPool is a build flag. Pool verification makes sure the integrity
|
||||
// of the integer pool by comparing values to a default value.
|
||||
if verifyPool {
|
||||
i.Set(checkVal)
|
||||
}
|
||||
p.pool.push(i)
|
||||
}
|
||||
}
|
||||
|
||||
// The intPool pool's default capacity
|
||||
const poolDefaultCap = 25
|
||||
|
||||
// intPoolPool manages a pool of intPools.
|
||||
type intPoolPool struct {
|
||||
pools []*intPool
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
var poolOfIntPools = &intPoolPool{
|
||||
pools: make([]*intPool, 0, poolDefaultCap),
|
||||
}
|
||||
|
||||
// get is looking for an available pool to return.
|
||||
func (ipp *intPoolPool) get() *intPool {
|
||||
ipp.lock.Lock()
|
||||
defer ipp.lock.Unlock()
|
||||
|
||||
if len(poolOfIntPools.pools) > 0 {
|
||||
ip := ipp.pools[len(ipp.pools)-1]
|
||||
ipp.pools = ipp.pools[:len(ipp.pools)-1]
|
||||
return ip
|
||||
}
|
||||
return newIntPool()
|
||||
}
|
||||
|
||||
// put a pool that has been allocated with get.
|
||||
func (ipp *intPoolPool) put(ip *intPool) {
|
||||
ipp.lock.Lock()
|
||||
defer ipp.lock.Unlock()
|
||||
|
||||
if len(ipp.pools) < cap(ipp.pools) {
|
||||
ipp.pools = append(ipp.pools, ip)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
// Copyright 2018 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 vm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIntPoolPoolGet(t *testing.T) {
|
||||
poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
|
||||
|
||||
nip := poolOfIntPools.get()
|
||||
if nip == nil {
|
||||
t.Fatalf("Invalid pool allocation")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntPoolPoolPut(t *testing.T) {
|
||||
poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
|
||||
|
||||
nip := poolOfIntPools.get()
|
||||
if len(poolOfIntPools.pools) != 0 {
|
||||
t.Fatalf("Pool got added to list when none should have been")
|
||||
}
|
||||
|
||||
poolOfIntPools.put(nip)
|
||||
if len(poolOfIntPools.pools) == 0 {
|
||||
t.Fatalf("Pool did not get added to list when one should have been")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntPoolPoolReUse(t *testing.T) {
|
||||
poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
|
||||
nip := poolOfIntPools.get()
|
||||
poolOfIntPools.put(nip)
|
||||
poolOfIntPools.get()
|
||||
|
||||
if len(poolOfIntPools.pools) != 0 {
|
||||
t.Fatalf("Invalid number of pools. Got %d, expected %d", len(poolOfIntPools.pools), 0)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -156,8 +156,8 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
|
|||
// it in the local storage container.
|
||||
if op == SSTORE && stack.len() >= 2 {
|
||||
var (
|
||||
value = common.BigToHash(stack.data[stack.len()-2])
|
||||
address = common.BigToHash(stack.data[stack.len()-1])
|
||||
value = common.Hash(stack.data[stack.len()-2].Bytes32())
|
||||
address = common.Hash(stack.data[stack.len()-1].Bytes32())
|
||||
)
|
||||
l.changedValues[contract.Address()][address] = value
|
||||
}
|
||||
|
|
@ -172,7 +172,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
|
|||
if !l.cfg.DisableStack {
|
||||
stck = make([]*big.Int, len(stack.Data()))
|
||||
for i, item := range stack.Data() {
|
||||
stck[i] = new(big.Int).Set(item)
|
||||
stck[i] = new(big.Int).Set(item.ToBig())
|
||||
}
|
||||
}
|
||||
// Copy a snapshot of the current storage to a new container
|
||||
|
|
|
|||
|
|
@ -62,14 +62,19 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
|
|||
log.Memory = memory.Data()
|
||||
}
|
||||
if !l.cfg.DisableStack {
|
||||
log.Stack = stack.Data()
|
||||
//TODO(@holiman) improve this
|
||||
logstack := make([]*big.Int, len(stack.Data()))
|
||||
for i, item := range stack.Data() {
|
||||
logstack[i] = item.ToBig()
|
||||
}
|
||||
log.Stack = logstack
|
||||
}
|
||||
return l.encoder.Encode(log)
|
||||
}
|
||||
|
||||
// CaptureFault outputs state information on the logger.
|
||||
func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||
return nil
|
||||
return l.CaptureState(env, pc, op, gas, cost, memory, stack, contract, depth, err)
|
||||
}
|
||||
|
||||
// CaptureEnd is triggered at end of execution.
|
||||
|
|
@ -80,8 +85,9 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration,
|
|||
Time time.Duration `json:"time"`
|
||||
Err string `json:"error,omitempty"`
|
||||
}
|
||||
var errMsg string
|
||||
if err != nil {
|
||||
return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, err.Error()})
|
||||
errMsg = err.Error()
|
||||
}
|
||||
return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, ""})
|
||||
return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, errMsg})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,17 +20,16 @@ import (
|
|||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
type dummyContractRef struct {
|
||||
calledForEach bool
|
||||
}
|
||||
|
||||
func (dummyContractRef) ReturnGas(*big.Int) {}
|
||||
func (dummyContractRef) Address() common.Address { return common.Address{} }
|
||||
func (dummyContractRef) Value() *big.Int { return new(big.Int) }
|
||||
func (dummyContractRef) SetCode(common.Hash, []byte) {}
|
||||
|
|
@ -57,8 +56,8 @@ func TestStoreCapture(t *testing.T) {
|
|||
stack = newstack()
|
||||
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0)
|
||||
)
|
||||
stack.push(big.NewInt(1))
|
||||
stack.push(big.NewInt(0))
|
||||
stack.push(uint256.NewInt(1))
|
||||
stack.push(new(uint256.Int))
|
||||
var index common.Hash
|
||||
logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, contract, 0, nil)
|
||||
if len(logger.changedValues[contract.Address()]) == 0 {
|
||||
|
|
|
|||
|
|
@ -17,10 +17,7 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// Memory implements a simple memory model for the ethereum virtual machine.
|
||||
|
|
@ -50,16 +47,15 @@ func (m *Memory) Set(offset, size uint64, value []byte) {
|
|||
|
||||
// Set32 sets the 32 bytes starting at offset to the value of val, left-padded with zeroes to
|
||||
// 32 bytes.
|
||||
func (m *Memory) Set32(offset uint64, val *big.Int) {
|
||||
func (m *Memory) Set32(offset uint64, val *uint256.Int) {
|
||||
// length of store may never be less than offset + size.
|
||||
// The store should be resized PRIOR to setting the memory
|
||||
if offset+32 > uint64(len(m.store)) {
|
||||
panic("invalid memory: store empty")
|
||||
}
|
||||
// Zero the memory area
|
||||
copy(m.store[offset:offset+32], []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
|
||||
// Fill in relevant bits
|
||||
math.ReadBits(val, m.store[offset:offset+32])
|
||||
b32 := val.Bytes32()
|
||||
copy(m.store[offset:], b32[:])
|
||||
}
|
||||
|
||||
// Resize resizes the memory to size
|
||||
|
|
@ -69,7 +65,7 @@ func (m *Memory) Resize(size uint64) {
|
|||
}
|
||||
}
|
||||
|
||||
// Get returns offset + size as a new slice
|
||||
// GetCopy returns offset + size as a new slice
|
||||
func (m *Memory) GetCopy(offset, size int64) (cpy []byte) {
|
||||
if size == 0 {
|
||||
return nil
|
||||
|
|
@ -107,18 +103,3 @@ func (m *Memory) Len() int {
|
|||
func (m *Memory) Data() []byte {
|
||||
return m.store
|
||||
}
|
||||
|
||||
// Print dumps the content of the memory.
|
||||
func (m *Memory) Print() {
|
||||
fmt.Printf("### mem %d bytes ###\n", len(m.store))
|
||||
if len(m.store) > 0 {
|
||||
addr := 0
|
||||
for i := 0; i+32 <= len(m.store); i += 32 {
|
||||
fmt.Printf("%03d: % x\n", addr, m.store[i:i+32])
|
||||
addr++
|
||||
}
|
||||
} else {
|
||||
fmt.Println("-- empty --")
|
||||
}
|
||||
fmt.Println("####################")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package vm
|
||||
|
||||
func memorySha3(stack *Stack) (uint64, bool) {
|
||||
func memoryKeccak256(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,103 +25,101 @@ type OpCode byte
|
|||
|
||||
// IsPush specifies if an opcode is a PUSH opcode.
|
||||
func (op OpCode) IsPush() bool {
|
||||
switch op {
|
||||
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsStaticJump specifies if an opcode is JUMP.
|
||||
func (op OpCode) IsStaticJump() bool {
|
||||
return op == JUMP
|
||||
return PUSH0 <= op && op <= PUSH32
|
||||
}
|
||||
|
||||
// 0x0 range - arithmetic ops.
|
||||
const (
|
||||
STOP OpCode = iota
|
||||
ADD
|
||||
MUL
|
||||
SUB
|
||||
DIV
|
||||
SDIV
|
||||
MOD
|
||||
SMOD
|
||||
ADDMOD
|
||||
MULMOD
|
||||
EXP
|
||||
SIGNEXTEND
|
||||
STOP OpCode = 0x0
|
||||
ADD OpCode = 0x1
|
||||
MUL OpCode = 0x2
|
||||
SUB OpCode = 0x3
|
||||
DIV OpCode = 0x4
|
||||
SDIV OpCode = 0x5
|
||||
MOD OpCode = 0x6
|
||||
SMOD OpCode = 0x7
|
||||
ADDMOD OpCode = 0x8
|
||||
MULMOD OpCode = 0x9
|
||||
EXP OpCode = 0xa
|
||||
SIGNEXTEND OpCode = 0xb
|
||||
)
|
||||
|
||||
// 0x10 range - comparison ops.
|
||||
const (
|
||||
LT OpCode = iota + 0x10
|
||||
GT
|
||||
SLT
|
||||
SGT
|
||||
EQ
|
||||
ISZERO
|
||||
AND
|
||||
OR
|
||||
XOR
|
||||
NOT
|
||||
BYTE
|
||||
SHL
|
||||
SHR
|
||||
SAR
|
||||
LT OpCode = 0x10
|
||||
GT OpCode = 0x11
|
||||
SLT OpCode = 0x12
|
||||
SGT OpCode = 0x13
|
||||
EQ OpCode = 0x14
|
||||
ISZERO OpCode = 0x15
|
||||
AND OpCode = 0x16
|
||||
OR OpCode = 0x17
|
||||
XOR OpCode = 0x18
|
||||
NOT OpCode = 0x19
|
||||
BYTE OpCode = 0x1a
|
||||
SHL OpCode = 0x1b
|
||||
SHR OpCode = 0x1c
|
||||
SAR OpCode = 0x1d
|
||||
)
|
||||
|
||||
SHA3 OpCode = 0x20
|
||||
// 0x20 range - crypto.
|
||||
const (
|
||||
KECCAK256 OpCode = 0x20
|
||||
)
|
||||
|
||||
// 0x30 range - closure state.
|
||||
const (
|
||||
ADDRESS OpCode = 0x30 + iota
|
||||
BALANCE
|
||||
ORIGIN
|
||||
CALLER
|
||||
CALLVALUE
|
||||
CALLDATALOAD
|
||||
CALLDATASIZE
|
||||
CALLDATACOPY
|
||||
CODESIZE
|
||||
CODECOPY
|
||||
GASPRICE
|
||||
EXTCODESIZE
|
||||
EXTCODECOPY
|
||||
RETURNDATASIZE
|
||||
RETURNDATACOPY
|
||||
EXTCODEHASH
|
||||
ADDRESS OpCode = 0x30
|
||||
BALANCE OpCode = 0x31
|
||||
ORIGIN OpCode = 0x32
|
||||
CALLER OpCode = 0x33
|
||||
CALLVALUE OpCode = 0x34
|
||||
CALLDATALOAD OpCode = 0x35
|
||||
CALLDATASIZE OpCode = 0x36
|
||||
CALLDATACOPY OpCode = 0x37
|
||||
CODESIZE OpCode = 0x38
|
||||
CODECOPY OpCode = 0x39
|
||||
GASPRICE OpCode = 0x3a
|
||||
EXTCODESIZE OpCode = 0x3b
|
||||
EXTCODECOPY OpCode = 0x3c
|
||||
RETURNDATASIZE OpCode = 0x3d
|
||||
RETURNDATACOPY OpCode = 0x3e
|
||||
EXTCODEHASH OpCode = 0x3f
|
||||
)
|
||||
|
||||
// 0x40 range - block operations.
|
||||
const (
|
||||
BLOCKHASH OpCode = 0x40 + iota
|
||||
COINBASE
|
||||
TIMESTAMP
|
||||
NUMBER
|
||||
DIFFICULTY
|
||||
GASLIMIT
|
||||
BLOCKHASH OpCode = 0x40
|
||||
COINBASE OpCode = 0x41
|
||||
TIMESTAMP OpCode = 0x42
|
||||
NUMBER OpCode = 0x43
|
||||
DIFFICULTY OpCode = 0x44
|
||||
RANDOM OpCode = 0x44 // Same as DIFFICULTY
|
||||
PREVRANDAO OpCode = 0x44 // Same as DIFFICULTY
|
||||
GASLIMIT OpCode = 0x45
|
||||
CHAINID OpCode = 0x46
|
||||
SELFBALANCE OpCode = 0x47
|
||||
BASEFEE OpCode = 0x48
|
||||
)
|
||||
|
||||
// 0x50 range - 'storage' and execution.
|
||||
const (
|
||||
POP OpCode = 0x50 + iota
|
||||
MLOAD
|
||||
MSTORE
|
||||
MSTORE8
|
||||
SLOAD
|
||||
SSTORE
|
||||
JUMP
|
||||
JUMPI
|
||||
PC
|
||||
MSIZE
|
||||
GAS
|
||||
JUMPDEST
|
||||
POP OpCode = 0x50
|
||||
MLOAD OpCode = 0x51
|
||||
MSTORE OpCode = 0x52
|
||||
MSTORE8 OpCode = 0x53
|
||||
SLOAD OpCode = 0x54
|
||||
SSTORE OpCode = 0x55
|
||||
JUMP OpCode = 0x56
|
||||
JUMPI OpCode = 0x57
|
||||
PC OpCode = 0x58
|
||||
MSIZE OpCode = 0x59
|
||||
GAS OpCode = 0x5a
|
||||
JUMPDEST OpCode = 0x5b
|
||||
PUSH0 OpCode = 0x5f
|
||||
)
|
||||
|
||||
// 0x60 range.
|
||||
// 0x60 range - pushes.
|
||||
const (
|
||||
PUSH1 OpCode = 0x60 + iota
|
||||
PUSH2
|
||||
|
|
@ -155,7 +153,11 @@ const (
|
|||
PUSH30
|
||||
PUSH31
|
||||
PUSH32
|
||||
DUP1
|
||||
)
|
||||
|
||||
// 0x80 range - dups.
|
||||
const (
|
||||
DUP1 = 0x80 + iota
|
||||
DUP2
|
||||
DUP3
|
||||
DUP4
|
||||
|
|
@ -171,7 +173,11 @@ const (
|
|||
DUP14
|
||||
DUP15
|
||||
DUP16
|
||||
SWAP1
|
||||
)
|
||||
|
||||
// 0x90 range - swaps.
|
||||
const (
|
||||
SWAP1 = 0x90 + iota
|
||||
SWAP2
|
||||
SWAP3
|
||||
SWAP4
|
||||
|
|
@ -198,28 +204,22 @@ const (
|
|||
LOG4
|
||||
)
|
||||
|
||||
// unofficial opcodes used for parsing.
|
||||
const (
|
||||
PUSH OpCode = 0xb0 + iota
|
||||
DUP
|
||||
SWAP
|
||||
)
|
||||
|
||||
// 0xf0 range - closures.
|
||||
const (
|
||||
CREATE OpCode = 0xf0 + iota
|
||||
CALL
|
||||
CALLCODE
|
||||
RETURN
|
||||
DELEGATECALL
|
||||
CREATE2
|
||||
CREATE OpCode = 0xf0
|
||||
CALL OpCode = 0xf1
|
||||
CALLCODE OpCode = 0xf2
|
||||
RETURN OpCode = 0xf3
|
||||
DELEGATECALL OpCode = 0xf4
|
||||
CREATE2 OpCode = 0xf5
|
||||
|
||||
STATICCALL OpCode = 0xfa
|
||||
REVERT OpCode = 0xfd
|
||||
INVALID OpCode = 0xfe
|
||||
SELFDESTRUCT OpCode = 0xff
|
||||
)
|
||||
|
||||
// Since the opcodes aren't all in order we can't use a regular slice.
|
||||
var opCodeToString = map[OpCode]string{
|
||||
var opCodeToString = [256]string{
|
||||
// 0x0 range - arithmetic ops.
|
||||
STOP: "STOP",
|
||||
ADD: "ADD",
|
||||
|
|
@ -251,7 +251,7 @@ var opCodeToString = map[OpCode]string{
|
|||
MULMOD: "MULMOD",
|
||||
|
||||
// 0x20 range - crypto.
|
||||
SHA3: "SHA3",
|
||||
KECCAK256: "KECCAK256",
|
||||
|
||||
// 0x30 range - closure state.
|
||||
ADDRESS: "ADDRESS",
|
||||
|
|
@ -276,10 +276,11 @@ var opCodeToString = map[OpCode]string{
|
|||
COINBASE: "COINBASE",
|
||||
TIMESTAMP: "TIMESTAMP",
|
||||
NUMBER: "NUMBER",
|
||||
DIFFICULTY: "DIFFICULTY",
|
||||
DIFFICULTY: "DIFFICULTY", // TODO rename to PREVRANDAO post merge
|
||||
GASLIMIT: "GASLIMIT",
|
||||
CHAINID: "CHAINID",
|
||||
SELFBALANCE: "SELFBALANCE",
|
||||
BASEFEE: "BASEFEE",
|
||||
|
||||
// 0x50 range - 'storage' and execution.
|
||||
POP: "POP",
|
||||
|
|
@ -296,6 +297,7 @@ var opCodeToString = map[OpCode]string{
|
|||
MSIZE: "MSIZE",
|
||||
GAS: "GAS",
|
||||
JUMPDEST: "JUMPDEST",
|
||||
PUSH0: "PUSH0",
|
||||
|
||||
// 0x60 range - push.
|
||||
PUSH1: "PUSH1",
|
||||
|
|
@ -379,20 +381,16 @@ var opCodeToString = map[OpCode]string{
|
|||
CREATE2: "CREATE2",
|
||||
STATICCALL: "STATICCALL",
|
||||
REVERT: "REVERT",
|
||||
INVALID: "INVALID",
|
||||
SELFDESTRUCT: "SELFDESTRUCT",
|
||||
|
||||
PUSH: "PUSH",
|
||||
DUP: "DUP",
|
||||
SWAP: "SWAP",
|
||||
}
|
||||
|
||||
func (op OpCode) String() string {
|
||||
str := opCodeToString[op]
|
||||
if len(str) == 0 {
|
||||
return fmt.Sprintf("opcode 0x%x not defined", int(op))
|
||||
if s := opCodeToString[op]; s != "" {
|
||||
return s
|
||||
}
|
||||
|
||||
return str
|
||||
return fmt.Sprintf("opcode %#x not defined", int(op))
|
||||
}
|
||||
|
||||
var stringToOp = map[string]OpCode{
|
||||
|
|
@ -422,7 +420,7 @@ var stringToOp = map[string]OpCode{
|
|||
"SAR": SAR,
|
||||
"ADDMOD": ADDMOD,
|
||||
"MULMOD": MULMOD,
|
||||
"SHA3": SHA3,
|
||||
"KECCAK256": KECCAK256,
|
||||
"ADDRESS": ADDRESS,
|
||||
"BALANCE": BALANCE,
|
||||
"ORIGIN": ORIGIN,
|
||||
|
|
@ -449,6 +447,7 @@ var stringToOp = map[string]OpCode{
|
|||
"DIFFICULTY": DIFFICULTY,
|
||||
"GASLIMIT": GASLIMIT,
|
||||
"SELFBALANCE": SELFBALANCE,
|
||||
"BASEFEE": BASEFEE,
|
||||
"POP": POP,
|
||||
"MLOAD": MLOAD,
|
||||
"MSTORE": MSTORE,
|
||||
|
|
@ -461,6 +460,7 @@ var stringToOp = map[string]OpCode{
|
|||
"MSIZE": MSIZE,
|
||||
"GAS": GAS,
|
||||
"JUMPDEST": JUMPDEST,
|
||||
"PUSH0": PUSH0,
|
||||
"PUSH1": PUSH1,
|
||||
"PUSH2": PUSH2,
|
||||
"PUSH3": PUSH3,
|
||||
|
|
@ -536,6 +536,7 @@ var stringToOp = map[string]OpCode{
|
|||
"RETURN": RETURN,
|
||||
"CALLCODE": CALLCODE,
|
||||
"REVERT": REVERT,
|
||||
"INVALID": INVALID,
|
||||
"SELFDESTRUCT": SELFDESTRUCT,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,37 +17,31 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// Stack is an object for basic stack operations. Items popped to the stack are
|
||||
// expected to be changed and modified. stack does not take care of adding newly
|
||||
// initialised objects.
|
||||
type Stack struct {
|
||||
data []*big.Int
|
||||
data []uint256.Int
|
||||
}
|
||||
|
||||
func newstack() *Stack {
|
||||
return &Stack{data: make([]*big.Int, 0, 1024)}
|
||||
return &Stack{data: make([]uint256.Int, 0, 16)}
|
||||
}
|
||||
|
||||
// Data returns the underlying big.Int array.
|
||||
func (st *Stack) Data() []*big.Int {
|
||||
// Data returns the underlying uint256.Int array.
|
||||
func (st *Stack) Data() []uint256.Int {
|
||||
return st.data
|
||||
}
|
||||
|
||||
func (st *Stack) push(d *big.Int) {
|
||||
func (st *Stack) push(d *uint256.Int) {
|
||||
// NOTE push limit (1024) is checked in baseCheck
|
||||
//stackItem := new(big.Int).Set(d)
|
||||
//st.data = append(st.data, stackItem)
|
||||
st.data = append(st.data, d)
|
||||
}
|
||||
func (st *Stack) pushN(ds ...*big.Int) {
|
||||
st.data = append(st.data, ds...)
|
||||
st.data = append(st.data, *d)
|
||||
}
|
||||
|
||||
func (st *Stack) pop() (ret *big.Int) {
|
||||
func (st *Stack) pop() (ret uint256.Int) {
|
||||
ret = st.data[len(st.data)-1]
|
||||
st.data = st.data[:len(st.data)-1]
|
||||
return
|
||||
|
|
@ -61,28 +55,15 @@ func (st *Stack) swap(n int) {
|
|||
st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n]
|
||||
}
|
||||
|
||||
func (st *Stack) dup(pool *intPool, n int) {
|
||||
st.push(pool.get().Set(st.data[st.len()-n]))
|
||||
func (st *Stack) dup(n int) {
|
||||
st.push(&st.data[st.len()-n])
|
||||
}
|
||||
|
||||
func (st *Stack) peek() *big.Int {
|
||||
return st.data[st.len()-1]
|
||||
func (st *Stack) peek() *uint256.Int {
|
||||
return &st.data[st.len()-1]
|
||||
}
|
||||
|
||||
// Back returns the n'th item in stack
|
||||
func (st *Stack) Back(n int) *big.Int {
|
||||
return st.data[st.len()-n-1]
|
||||
}
|
||||
|
||||
// Print dumps the content of the stack
|
||||
func (st *Stack) Print() {
|
||||
fmt.Println("### stack ###")
|
||||
if len(st.data) > 0 {
|
||||
for i, val := range st.data {
|
||||
fmt.Printf("%-3d %v\n", i, val)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("-- empty --")
|
||||
}
|
||||
fmt.Println("#############")
|
||||
func (st *Stack) Back(n int) *uint256.Int {
|
||||
return &st.data[st.len()-n-1]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
"result": {
|
||||
"calls": [
|
||||
{
|
||||
"error": "invalid opcode: opcode 0xfe not defined",
|
||||
"error": "invalid opcode: INVALID",
|
||||
"from": "0x33056b5dcac09a9b4becad0e1dcf92c19bd0af76",
|
||||
"gas": "0x75fe3",
|
||||
"gasUsed": "0x75fe3",
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ func (sw *stackWrapper) peek(idx int) *big.Int {
|
|||
log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx)
|
||||
return new(big.Int)
|
||||
}
|
||||
return sw.stack.Data()[len(sw.stack.Data())-idx-1]
|
||||
return sw.stack.Back(idx).ToBig()
|
||||
}
|
||||
|
||||
// pushObject assembles a JSVM object wrapping a swappable stack and pushes it
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ func (account) SetBalance(*big.Int) {}
|
|||
func (account) SetNonce(uint64) {}
|
||||
func (account) Balance() *big.Int { return nil }
|
||||
func (account) Address() common.Address { return common.Address{} }
|
||||
func (account) ReturnGas(*big.Int) {}
|
||||
func (account) SetCode(common.Hash, []byte) {}
|
||||
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
||||
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -20,6 +20,7 @@ require (
|
|||
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/hashicorp/golang-lru v0.5.3
|
||||
github.com/holiman/uint256 v1.2.4
|
||||
github.com/huin/goupnp v1.3.0
|
||||
github.com/influxdata/influxdb v1.7.9
|
||||
github.com/jackpal/go-nat-pmp v1.0.2
|
||||
|
|
@ -61,7 +62,6 @@ require (
|
|||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/holiman/uint256 v1.2.3 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e // indirect
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -140,6 +140,8 @@ github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8
|
|||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o=
|
||||
github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw=
|
||||
github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
|
||||
github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag=
|
||||
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
|
||||
|
|
|
|||
|
|
@ -623,6 +623,24 @@ func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Addres
|
|||
return code, state.Error()
|
||||
}
|
||||
|
||||
// GetAccountInfo returns the information at the given address in the state for the given block number.
|
||||
func (s *PublicBlockChainAPI) GetAccountInfo(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (map[string]interface{}, error) {
|
||||
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
|
||||
if state == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info := state.GetAccountInfo(address)
|
||||
result := map[string]interface{}{
|
||||
"address": address,
|
||||
"balance": (*hexutil.Big)(info.Balance),
|
||||
"codeSize": info.CodeSize,
|
||||
"codeHash": info.CodeHash,
|
||||
"nonce": info.Nonce,
|
||||
"storageHash": info.StorageHash,
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetStorageAt returns the storage from the state at the given address, key and
|
||||
// block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block
|
||||
// numbers are also allowed.
|
||||
|
|
|
|||
|
|
@ -5329,6 +5329,13 @@ var methods = function () {
|
|||
inputFormatter: [formatters.inputAddressFormatter, formatters.inputDefaultBlockNumberFormatter]
|
||||
});
|
||||
|
||||
var getAccountInfo = new Method({
|
||||
name: 'getAccountInfo',
|
||||
call: 'eth_getAccountInfo',
|
||||
params: 2,
|
||||
inputFormatter: [formatters.inputAddressFormatter, formatters.inputDefaultBlockNumberFormatter]
|
||||
});
|
||||
|
||||
var getBlock = new Method({
|
||||
name: 'getBlock',
|
||||
call: blockCall,
|
||||
|
|
@ -5513,6 +5520,7 @@ var methods = function () {
|
|||
getBalance,
|
||||
getStorageAt,
|
||||
getCode,
|
||||
getAccountInfo,
|
||||
getBlock,
|
||||
getBlockSigners,
|
||||
getStakerROI,
|
||||
|
|
|
|||
|
|
@ -652,7 +652,7 @@ func (self *worker) commitNewWork() {
|
|||
log.Warn("Can't find coinbase account wallet", "coinbase", self.coinbase, "err", err)
|
||||
return
|
||||
}
|
||||
if self.config.XDPoS != nil && self.chain.Config().IsTIPXDCX(header.Number) {
|
||||
if self.config.XDPoS != nil && self.chain.Config().IsTIPXDCXMiner(header.Number) {
|
||||
XDCX := self.eth.GetXDCX()
|
||||
XDCXLending := self.eth.GetXDCXLending()
|
||||
if XDCX != nil && header.Number.Uint64() > self.config.XDPoS.Epoch {
|
||||
|
|
@ -710,8 +710,13 @@ func (self *worker) commitNewWork() {
|
|||
if XDCX.IsSDKNode() {
|
||||
self.chain.AddMatchingResult(tradingTransaction.Hash(), tradingMatchingResults)
|
||||
}
|
||||
// force adding trading, lending transaction to this block
|
||||
if tradingTransaction != nil {
|
||||
specialTxs = append(specialTxs, tradingTransaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(lendingInput) > 0 {
|
||||
// lending transaction
|
||||
lendingBatch := &lendingstate.TxLendingBatch{
|
||||
|
|
@ -735,6 +740,9 @@ func (self *worker) commitNewWork() {
|
|||
if XDCX.IsSDKNode() {
|
||||
self.chain.AddLendingResult(lendingTransaction.Hash(), lendingMatchingResults)
|
||||
}
|
||||
if lendingTransaction != nil {
|
||||
specialTxs = append(specialTxs, lendingTransaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -756,32 +764,23 @@ func (self *worker) commitNewWork() {
|
|||
if XDCX.IsSDKNode() {
|
||||
self.chain.AddFinalizedTrades(lendingFinalizedTradeTransaction.Hash(), updatedTrades)
|
||||
}
|
||||
if lendingFinalizedTradeTransaction != nil {
|
||||
specialTxs = append(specialTxs, lendingFinalizedTradeTransaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
XDCxStateRoot := work.tradingState.IntermediateRoot()
|
||||
LendingStateRoot := work.lendingState.IntermediateRoot()
|
||||
txData := append(XDCxStateRoot.Bytes(), LendingStateRoot.Bytes()...)
|
||||
tx := types.NewTransaction(work.state.GetNonce(self.coinbase), common.HexToAddress(common.TradingStateAddr), big.NewInt(0), txMatchGasLimit, big.NewInt(0), txData)
|
||||
txStateRoot, err := wallet.SignTx(accounts.Account{Address: self.coinbase}, tx, self.config.ChainId)
|
||||
if err != nil {
|
||||
log.Error("Fail to create tx state root", "error", err)
|
||||
return
|
||||
}
|
||||
specialTxs = append(specialTxs, txStateRoot)
|
||||
}
|
||||
|
||||
// force adding trading, lending transaction to this block
|
||||
if tradingTransaction != nil {
|
||||
specialTxs = append(specialTxs, tradingTransaction)
|
||||
}
|
||||
if lendingTransaction != nil {
|
||||
specialTxs = append(specialTxs, lendingTransaction)
|
||||
}
|
||||
if lendingFinalizedTradeTransaction != nil {
|
||||
specialTxs = append(specialTxs, lendingFinalizedTradeTransaction)
|
||||
}
|
||||
|
||||
XDCxStateRoot := work.tradingState.IntermediateRoot()
|
||||
LendingStateRoot := work.lendingState.IntermediateRoot()
|
||||
txData := append(XDCxStateRoot.Bytes(), LendingStateRoot.Bytes()...)
|
||||
tx := types.NewTransaction(work.state.GetNonce(self.coinbase), common.HexToAddress(common.TradingStateAddr), big.NewInt(0), txMatchGasLimit, big.NewInt(0), txData)
|
||||
txStateRoot, err := wallet.SignTx(accounts.Account{Address: self.coinbase}, tx, self.config.ChainId)
|
||||
if err != nil {
|
||||
log.Error("Fail to create tx state root", "error", err)
|
||||
return
|
||||
}
|
||||
specialTxs = append(specialTxs, txStateRoot)
|
||||
}
|
||||
work.commitTransactions(self.mux, feeCapacity, txs, specialTxs, self.chain, self.coinbase)
|
||||
// compute uncles for the new block.
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ public class AndroidTest extends InstrumentationTestCase {
|
|||
//
|
||||
// This method has been adapted from golang.org/x/mobile/bind/java/seq_test.go/runTest
|
||||
func TestAndroid(t *testing.T) {
|
||||
t.Skip("skip this test since it's not being used")
|
||||
// Skip tests on Windows altogether
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("cannot test Android bindings on Windows, skipping")
|
||||
|
|
|
|||
136
params/config.go
136
params/config.go
|
|
@ -241,31 +241,74 @@ var (
|
|||
//
|
||||
// This configuration is intentionally not using keyed fields to force anyone
|
||||
// adding flags to the config to also have to set these fields.
|
||||
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil}
|
||||
AllEthashProtocolChanges = &ChainConfig{
|
||||
ChainId: big.NewInt(1337),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
DAOForkBlock: nil,
|
||||
DAOForkSupport: false,
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP150Hash: common.Hash{},
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: nil,
|
||||
Ethash: new(EthashConfig),
|
||||
Clique: nil,
|
||||
XDPoS: nil,
|
||||
}
|
||||
|
||||
// AllXDPoSProtocolChanges contains every protocol change (EIPs) introduced
|
||||
// and accepted by the Ethereum core developers into the XDPoS consensus.
|
||||
//
|
||||
// This configuration is intentionally not using keyed fields to force anyone
|
||||
// adding flags to the config to also have to set these fields.
|
||||
AllXDPoSProtocolChanges = &ChainConfig{big.NewInt(89), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &XDPoSConfig{Period: 0, Epoch: 900}}
|
||||
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 900}, nil}
|
||||
AllXDPoSProtocolChanges = &ChainConfig{
|
||||
ChainId: big.NewInt(89),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
DAOForkBlock: nil,
|
||||
DAOForkSupport: false,
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP150Hash: common.Hash{},
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: nil,
|
||||
Ethash: nil,
|
||||
Clique: nil,
|
||||
XDPoS: &XDPoSConfig{Period: 0, Epoch: 900},
|
||||
}
|
||||
|
||||
AllCliqueProtocolChanges = &ChainConfig{
|
||||
ChainId: big.NewInt(1337),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
DAOForkBlock: nil,
|
||||
DAOForkSupport: false,
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP150Hash: common.Hash{},
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: nil,
|
||||
Ethash: nil,
|
||||
Clique: &CliqueConfig{Period: 0, Epoch: 900},
|
||||
XDPoS: nil,
|
||||
}
|
||||
|
||||
// XDPoS config with v2 engine after block 901
|
||||
TestXDPoSMockChainConfig = &ChainConfig{
|
||||
big.NewInt(1337),
|
||||
big.NewInt(0),
|
||||
nil,
|
||||
false,
|
||||
big.NewInt(0),
|
||||
common.Hash{},
|
||||
big.NewInt(0),
|
||||
big.NewInt(0),
|
||||
big.NewInt(0),
|
||||
nil,
|
||||
new(EthashConfig),
|
||||
nil,
|
||||
&XDPoSConfig{
|
||||
ChainId: big.NewInt(1337),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
DAOForkBlock: nil,
|
||||
DAOForkSupport: false,
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP150Hash: common.Hash{},
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: nil,
|
||||
Ethash: new(EthashConfig),
|
||||
Clique: nil,
|
||||
XDPoS: &XDPoSConfig{
|
||||
Epoch: 900,
|
||||
Gap: 450,
|
||||
SkipV1Validation: true,
|
||||
|
|
@ -279,8 +322,22 @@ var (
|
|||
},
|
||||
}
|
||||
|
||||
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil}
|
||||
TestRules = TestChainConfig.Rules(new(big.Int))
|
||||
TestChainConfig = &ChainConfig{
|
||||
ChainId: big.NewInt(1),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
DAOForkBlock: nil,
|
||||
DAOForkSupport: false,
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP150Hash: common.Hash{},
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: nil,
|
||||
Ethash: new(EthashConfig),
|
||||
Clique: nil,
|
||||
XDPoS: nil,
|
||||
}
|
||||
TestRules = TestChainConfig.Rules(new(big.Int))
|
||||
)
|
||||
|
||||
// ChainConfig is the core config which determines the blockchain settings.
|
||||
|
|
@ -441,7 +498,7 @@ func (c *ChainConfig) String() string {
|
|||
default:
|
||||
engine = "unknown"
|
||||
}
|
||||
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Engine: %v}",
|
||||
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Istanbul: %v BerlinBlock: %v LondonBlock: %v MergeBlock: %v ShanghaiBlock: %v Engine: %v}",
|
||||
c.ChainId,
|
||||
c.HomesteadBlock,
|
||||
c.DAOForkBlock,
|
||||
|
|
@ -451,6 +508,11 @@ func (c *ChainConfig) String() string {
|
|||
c.EIP158Block,
|
||||
c.ByzantiumBlock,
|
||||
c.ConstantinopleBlock,
|
||||
common.TIPXDCXCancellationFee,
|
||||
common.BerlinBlock,
|
||||
common.LondonBlock,
|
||||
common.MergeBlock,
|
||||
common.ShanghaiBlock,
|
||||
engine,
|
||||
)
|
||||
}
|
||||
|
|
@ -497,6 +559,27 @@ func (c *ChainConfig) IsIstanbul(num *big.Int) bool {
|
|||
return isForked(common.TIPXDCXCancellationFee, num)
|
||||
}
|
||||
|
||||
// IsBerlin returns whether num is either equal to the Berlin fork block or greater.
|
||||
func (c *ChainConfig) IsBerlin(num *big.Int) bool {
|
||||
return isForked(common.BerlinBlock, num)
|
||||
}
|
||||
|
||||
// IsLondon returns whether num is either equal to the London fork block or greater.
|
||||
func (c *ChainConfig) IsLondon(num *big.Int) bool {
|
||||
return isForked(common.LondonBlock, num)
|
||||
}
|
||||
|
||||
// IsMerge returns whether num is either equal to the Merge fork block or greater.
|
||||
// Different from Geth which uses `block.difficulty != nil`
|
||||
func (c *ChainConfig) IsMerge(num *big.Int) bool {
|
||||
return isForked(common.MergeBlock, num)
|
||||
}
|
||||
|
||||
// IsShanghai returns whether num is either equal to the Shanghai fork block or greater.
|
||||
func (c *ChainConfig) IsShanghai(num *big.Int) bool {
|
||||
return isForked(common.ShanghaiBlock, num)
|
||||
}
|
||||
|
||||
func (c *ChainConfig) IsTIP2019(num *big.Int) bool {
|
||||
return isForked(common.TIP2019Block, num)
|
||||
}
|
||||
|
|
@ -520,11 +603,10 @@ func (c *ChainConfig) IsTIPNoHalvingMNReward(num *big.Int) bool {
|
|||
return isForked(common.TIPNoHalvingMNReward, num)
|
||||
}
|
||||
func (c *ChainConfig) IsTIPXDCX(num *big.Int) bool {
|
||||
if common.IsTestnet {
|
||||
return isForked(common.TIPXDCXTestnet, num)
|
||||
} else {
|
||||
return isForked(common.TIPXDCX, num)
|
||||
}
|
||||
return isForked(common.TIPXDCX, num)
|
||||
}
|
||||
func (c *ChainConfig) IsTIPXDCXMiner(num *big.Int) bool {
|
||||
return isForked(common.TIPXDCX, num) && !isForked(common.TIPXDCXDISABLE, num)
|
||||
}
|
||||
|
||||
func (c *ChainConfig) IsTIPXDCXLending(num *big.Int) bool {
|
||||
|
|
@ -665,6 +747,8 @@ type Rules struct {
|
|||
ChainId *big.Int
|
||||
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
|
||||
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
|
||||
IsBerlin, IsLondon bool
|
||||
IsMerge, IsShanghai bool
|
||||
}
|
||||
|
||||
func (c *ChainConfig) Rules(num *big.Int) Rules {
|
||||
|
|
@ -682,5 +766,9 @@ func (c *ChainConfig) Rules(num *big.Int) Rules {
|
|||
IsConstantinople: c.IsConstantinople(num),
|
||||
IsPetersburg: c.IsPetersburg(num),
|
||||
IsIstanbul: c.IsIstanbul(num),
|
||||
IsBerlin: c.IsBerlin(num),
|
||||
IsLondon: c.IsLondon(num),
|
||||
IsMerge: c.IsMerge(num),
|
||||
IsShanghai: c.IsShanghai(num),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ const (
|
|||
LogDataGas uint64 = 8 // Per byte in a LOG* operation's data.
|
||||
CallStipend uint64 = 2300 // Free gas given at beginning of call.
|
||||
|
||||
Sha3Gas uint64 = 30 // Once per SHA3 operation.
|
||||
Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data.
|
||||
Keccak256Gas uint64 = 30 // Once per KECCAK256 operation.
|
||||
Keccak256WordGas uint64 = 6 // Once per word of the KECCAK256 operation's data.
|
||||
SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero.
|
||||
SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change.
|
||||
SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero.
|
||||
|
|
@ -98,14 +98,10 @@ const (
|
|||
NetSstoreResetRefund uint64 = 4800 // Once per SSTORE operation for resetting to the original non-zero value
|
||||
NetSstoreResetClearRefund uint64 = 19800 // Once per SSTORE operation for resetting to the original zero value
|
||||
|
||||
SstoreSentryGasEIP2200 uint64 = 2300 // Minimum gas required to be present for an SSTORE call, not consumed
|
||||
SstoreNoopGasEIP2200 uint64 = 800 // Once per SSTORE operation if the value doesn't change.
|
||||
SstoreDirtyGasEIP2200 uint64 = 800 // Once per SSTORE operation if a dirty value is changed.
|
||||
SstoreInitGasEIP2200 uint64 = 20000 // Once per SSTORE operation from clean zero to non-zero
|
||||
SstoreInitRefundEIP2200 uint64 = 19200 // Once per SSTORE operation for resetting to the original zero value
|
||||
SstoreCleanGasEIP2200 uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else
|
||||
SstoreCleanRefundEIP2200 uint64 = 4200 // Once per SSTORE operation for resetting to the original non-zero value
|
||||
SstoreClearRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot
|
||||
SstoreSentryGasEIP2200 uint64 = 2300 // Minimum gas required to be present for an SSTORE call, not consumed
|
||||
SstoreSetGasEIP2200 uint64 = 20000 // Once per SSTORE operation from clean zero to non-zero
|
||||
SstoreResetGasEIP2200 uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else
|
||||
SstoreClearsScheduleRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot
|
||||
|
||||
Create2Gas uint64 = 32000 // Once per CREATE2 operation
|
||||
SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation.
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import (
|
|||
|
||||
const (
|
||||
VersionMajor = 2 // Major version component of the current release
|
||||
VersionMinor = 0 // Minor version component of the current release
|
||||
VersionPatch = 2 // Patch version component of the current release
|
||||
VersionMinor = 1 // Minor version component of the current release
|
||||
VersionPatch = 0 // Patch version component of the current release
|
||||
VersionMeta = "beta1" // Version metadata to append to the version string
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -237,11 +237,12 @@ func NewHTTPServer(cors []string, vhosts []string, srv *Server, writeTimeout tim
|
|||
// Wrap the CORS-handler within a host-handler
|
||||
handler := newCorsHandler(srv, cors)
|
||||
handler = newVHostHandler(vhosts, handler)
|
||||
handler = http.TimeoutHandler(handler, writeTimeout, `{"error":"http server timeout"}`)
|
||||
log.Info("NewHTTPServer", "writeTimeout", writeTimeout)
|
||||
return &http.Server{
|
||||
Handler: handler,
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: writeTimeout,
|
||||
WriteTimeout: writeTimeout + time.Second,
|
||||
IdleTimeout: 120 * time.Second,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ import (
|
|||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"math/big"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
|
|
@ -143,7 +143,6 @@ func (t *VMTest) newEVM(statedb *state.StateDB, vmconfig vm.Config) *vm.EVM {
|
|||
Difficulty: t.json.Env.Difficulty,
|
||||
GasPrice: t.json.Exec.GasPrice,
|
||||
}
|
||||
vmconfig.NoRecursion = true
|
||||
return vm.NewEVM(context, statedb, nil, params.MainnetChainConfig, vmconfig)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue