diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 000000000000..0aeeb8f0e3fa
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -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}"
diff --git a/.gitignore b/.gitignore
index 5548c132dd29..1a85f2f558a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml.bak
similarity index 100%
rename from .travis.yml
rename to .travis.yml.bak
diff --git a/accounts/keystore/file_cache.go b/accounts/keystore/file_cache.go
index ce2d5fc91a6c..0ef6e28dc7d1 100644
--- a/accounts/keystore/file_cache.go
+++ b/accounts/keystore/file_cache.go
@@ -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).
diff --git a/cicd/README.md b/cicd/README.md
index 77d22d538701..b5d61e4098ec 100644
--- a/cicd/README.md
+++ b/cicd/README.md
@@ -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
``
\ No newline at end of file
diff --git a/cicd/devnet/start.sh b/cicd/devnet/start.sh
index 8899d16fa2bf..d5f7e152f490 100755
--- a/cicd/devnet/start.sh
+++ b/cicd/devnet/start.sh
@@ -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
diff --git a/cicd/devnet/terraform/module/region/container-definition.tpl b/cicd/devnet/terraform/module/region/container-definition.tpl
index 270a6847fc5a..008e98522ac1 100644
--- a/cicd/devnet/terraform/module/region/container-definition.tpl
+++ b/cicd/devnet/terraform/module/region/container-definition.tpl
@@ -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}"}
diff --git a/cicd/devnet/terraform/module/region/ecs.tf b/cicd/devnet/terraform/module/region/ecs.tf
index 0f589f944728..8cfc43427e68 100644
--- a/cicd/devnet/terraform/module/region/ecs.tf
+++ b/cicd/devnet/terraform/module/region/ecs.tf
@@ -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}")}"
diff --git a/cicd/mainnet/start.sh b/cicd/mainnet/start.sh
index c9512a804ce2..35d53ff60188 100755
--- a/cicd/mainnet/start.sh
+++ b/cicd/mainnet/start.sh
@@ -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
diff --git a/cicd/terraform/.env b/cicd/terraform/.env
new file mode 100644
index 000000000000..4eb6ca5a95c0
--- /dev/null
+++ b/cicd/terraform/.env
@@ -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
\ No newline at end of file
diff --git a/cicd/terraform/iam.tf b/cicd/terraform/iam.tf
new file mode 100644
index 000000000000..f5c5ee2fe08c
--- /dev/null
+++ b/cicd/terraform/iam.tf
@@ -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
+}
diff --git a/cicd/terraform/main.tf b/cicd/terraform/main.tf
new file mode 100644
index 000000000000..5df86c7c2bd1
--- /dev/null
+++ b/cicd/terraform/main.tf
@@ -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
+ }
+}
+
diff --git a/cicd/terraform/module/region/container-definition.tpl b/cicd/terraform/module/region/container-definition.tpl
new file mode 100644
index 000000000000..008e98522ac1
--- /dev/null
+++ b/cicd/terraform/module/region/container-definition.tpl
@@ -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"
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/cicd/terraform/module/region/ecs.tf b/cicd/terraform/module/region/ecs.tf
new file mode 100644
index 000000000000..d529cafb4702
--- /dev/null
+++ b/cicd/terraform/module/region/ecs.tf
@@ -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}"
+ }
+}
\ No newline at end of file
diff --git a/cicd/terraform/module/region/efs.tf b/cicd/terraform/module/region/efs.tf
new file mode 100644
index 000000000000..11b426ff3754
--- /dev/null
+++ b/cicd/terraform/module/region/efs.tf
@@ -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}"
+ }
+}
\ No newline at end of file
diff --git a/cicd/terraform/module/region/main.tf b/cicd/terraform/module/region/main.tf
new file mode 100644
index 000000000000..5c6e0a47cd16
--- /dev/null
+++ b/cicd/terraform/module/region/main.tf
@@ -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}"
+ }
+}
\ No newline at end of file
diff --git a/cicd/terraform/module/region/rpc.tf b/cicd/terraform/module/region/rpc.tf
new file mode 100644
index 000000000000..901b3b9c0bca
--- /dev/null
+++ b/cicd/terraform/module/region/rpc.tf
@@ -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
+ }
+}
\ No newline at end of file
diff --git a/cicd/terraform/module/region/variables.tf b/cicd/terraform/module/region/variables.tf
new file mode 100644
index 000000000000..3f09785b8142
--- /dev/null
+++ b/cicd/terraform/module/region/variables.tf
@@ -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
+}
\ No newline at end of file
diff --git a/cicd/terraform/s3.tf b/cicd/terraform/s3.tf
new file mode 100644
index 000000000000..4968c852c33f
--- /dev/null
+++ b/cicd/terraform/s3.tf
@@ -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"
+}
diff --git a/cicd/terraform/variables.tf b/cicd/terraform/variables.tf
new file mode 100644
index 000000000000..d952bc258cf4
--- /dev/null
+++ b/cicd/terraform/variables.tf
@@ -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
+}
diff --git a/cicd/testnet/start.sh b/cicd/testnet/start.sh
index 6050ac990c34..3c5b2234a560 100755
--- a/cicd/testnet/start.sh
+++ b/cicd/testnet/start.sh
@@ -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
diff --git a/cmd/XDC/chaincmd.go b/cmd/XDC/chaincmd.go
index 2f18dae19600..ae4221e4b211 100644
--- a/cmd/XDC/chaincmd.go
+++ b/cmd/XDC/chaincmd.go
@@ -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
}
diff --git a/cmd/evm/json_logger.go b/cmd/evm/json_logger.go
index 458d56494b30..90e44f9c4ae5 100644
--- a/cmd/evm/json_logger.go
+++ b/cmd/evm/json_logger.go
@@ -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)
}
diff --git a/common/constants.go b/common/constants.go
index 71b1093cf05e..803443a7814c 100644
--- a/common/constants.go
+++ b/common/constants.go
@@ -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
diff --git a/common/constants/constants.go.devnet b/common/constants/constants.go.devnet
index c121d305239f..81951a760cd8 100644
--- a/common/constants/constants.go.devnet
+++ b/common/constants/constants.go.devnet
@@ -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
diff --git a/common/constants/constants.go.testnet b/common/constants/constants.go.testnet
index 0c1db046cfb8..3b2811ade92b 100644
--- a/common/constants/constants.go.testnet
+++ b/common/constants/constants.go.testnet
@@ -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
diff --git a/common/lru/basiclru.go b/common/lru/basiclru.go
new file mode 100644
index 000000000000..a429157fe50a
--- /dev/null
+++ b/common/lru/basiclru.go
@@ -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 .
+
+// 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
+}
diff --git a/common/lru/basiclru_test.go b/common/lru/basiclru_test.go
new file mode 100644
index 000000000000..29812bda157a
--- /dev/null
+++ b/common/lru/basiclru_test.go
@@ -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 .
+
+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)
+}
diff --git a/common/lru/blob_lru.go b/common/lru/blob_lru.go
new file mode 100644
index 000000000000..c9b33985032c
--- /dev/null
+++ b/common/lru/blob_lru.go
@@ -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 .
+
+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)
+}
diff --git a/common/lru/blob_lru_test.go b/common/lru/blob_lru_test.go
new file mode 100644
index 000000000000..ca1b0ddd742a
--- /dev/null
+++ b/common/lru/blob_lru_test.go
@@ -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 .
+
+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)
+ }
+ }
+}
diff --git a/common/lru/lru.go b/common/lru/lru.go
new file mode 100644
index 000000000000..45965adb0dfc
--- /dev/null
+++ b/common/lru/lru.go
@@ -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 .
+
+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()
+}
diff --git a/consensus/XDPoS/engines/engine_v1/engine.go b/consensus/XDPoS/engines/engine_v1/engine.go
index f01f486aa702..7fce2e6a8676 100644
--- a/consensus/XDPoS/engines/engine_v1/engine.go
+++ b/consensus/XDPoS/engines/engine_v1/engine.go
@@ -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 {
diff --git a/consensus/XDPoS/engines/engine_v2/timeout.go b/consensus/XDPoS/engines/engine_v2/timeout.go
index d03f2d656a16..d53ed386ee16 100644
--- a/consensus/XDPoS/engines/engine_v2/timeout.go
+++ b/consensus/XDPoS/engines/engine_v2/timeout.go
@@ -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)
diff --git a/core/asm/asm_test.go b/core/asm/asm_test.go
index 92b26b67a5ca..cd7520ec6394 100644
--- a/core/asm/asm_test.go
+++ b/core/asm/asm_test.go
@@ -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")
-
- 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)
+// 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
+
+ } {
+ 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)
+ }
}
}
diff --git a/core/evm.go b/core/evm.go
index f3fb1a234971..bc1c72ce5bc2 100644
--- a/core/evm.go
+++ b/core/evm.go
@@ -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,
}
}
diff --git a/core/state/state_object.go b/core/state/state_object.go
index 53ea774d6194..a5c7ce0bc964 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -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.
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 07bcf7596b54..4bedebe8b3c3 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -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 {
diff --git a/core/types/consensus_v2.go b/core/types/consensus_v2.go
index 00db4c9488b6..8dae6aaa0728 100644
--- a/core/types/consensus_v2.go
+++ b/core/types/consensus_v2.go
@@ -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
diff --git a/core/vm/analysis.go b/core/vm/analysis.go
index 449cded2a896..3733bab6a7c0 100644
--- a/core/vm/analysis.go
+++ b/core/vm/analysis.go
@@ -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.
diff --git a/core/vm/analysis_test.go b/core/vm/analysis_test.go
index 99e4e386b404..20674bf919d3 100644
--- a/core/vm/analysis_test.go
+++ b/core/vm/analysis_test.go
@@ -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)
diff --git a/core/vm/common.go b/core/vm/common.go
index 194e3897f8c3..bb11f4a393e5 100644
--- a/core/vm/common.go
+++ b/core/vm/common.go
@@ -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 {
diff --git a/core/vm/contract.go b/core/vm/contract.go
index cc9d59cf2deb..2be61b51d0ce 100644
--- a/core/vm/contract.go
+++ b/core/vm/contract.go
@@ -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.
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index 28394d4dd23c..067bc7018ab4 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -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
diff --git a/core/vm/eips.go b/core/vm/eips.go
index 40da1f88fafa..48ffb0d6917e 100644
--- a/core/vm/eips.go
+++ b/core/vm/eips.go
@@ -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
+}
diff --git a/core/vm/errors.go b/core/vm/errors.go
index c813aa36af36..236e22568b58 100644
--- a/core/vm/errors.go
+++ b/core/vm/errors.go
@@ -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
diff --git a/core/vm/evm.go b/core/vm/evm.go
index f6f6a29a87d0..227d38412c86 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -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}
diff --git a/core/vm/gas.go b/core/vm/gas.go
index bda326cdc7ab..5cf1d852d24a 100644
--- a/core/vm/gas.go
+++ b/core/vm/gas.go
@@ -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
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index 987dca32384d..45589157bb00 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -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
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 4f1d12291132..a020654440c6 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -17,303 +17,172 @@
package vm
import (
- "math/big"
-
- "github.com/XinFinOrg/XDPoSChain/params"
+ "sync/atomic"
"github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/common/math"
"github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/XinFinOrg/XDPoSChain/params"
+ "github.com/holiman/uint256"
"golang.org/x/crypto/sha3"
)
-var (
- bigZero = new(big.Int)
- tt255 = math.BigPow(2, 255)
-)
-
func opAdd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
- math.U256(y.Add(x, y))
-
- interpreter.intPool.putOne(x)
+ y.Add(&x, y)
return nil, nil
}
func opSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
- math.U256(y.Sub(x, y))
-
- interpreter.intPool.putOne(x)
+ y.Sub(&x, y)
return nil, nil
}
func opMul(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.pop()
- callContext.stack.push(math.U256(x.Mul(x, y)))
-
- interpreter.intPool.putOne(y)
-
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Mul(&x, y)
return nil, nil
}
func opDiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
- if y.Sign() != 0 {
- math.U256(y.Div(x, y))
- } else {
- y.SetUint64(0)
- }
- interpreter.intPool.putOne(x)
+ y.Div(&x, y)
return nil, nil
}
func opSdiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := math.S256(callContext.stack.pop()), math.S256(callContext.stack.pop())
- res := interpreter.intPool.getZero()
-
- if y.Sign() == 0 || x.Sign() == 0 {
- callContext.stack.push(res)
- } else {
- if x.Sign() != y.Sign() {
- res.Div(x.Abs(x), y.Abs(y))
- res.Neg(res)
- } else {
- res.Div(x.Abs(x), y.Abs(y))
- }
- callContext.stack.push(math.U256(res))
- }
- interpreter.intPool.put(x, y)
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.SDiv(&x, y)
return nil, nil
}
func opMod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.pop()
- if y.Sign() == 0 {
- callContext.stack.push(x.SetUint64(0))
- } else {
- callContext.stack.push(math.U256(x.Mod(x, y)))
- }
- interpreter.intPool.putOne(y)
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Mod(&x, y)
return nil, nil
}
func opSmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := math.S256(callContext.stack.pop()), math.S256(callContext.stack.pop())
- res := interpreter.intPool.getZero()
-
- if y.Sign() == 0 {
- callContext.stack.push(res)
- } else {
- if x.Sign() < 0 {
- res.Mod(x.Abs(x), y.Abs(y))
- res.Neg(res)
- } else {
- res.Mod(x.Abs(x), y.Abs(y))
- }
- callContext.stack.push(math.U256(res))
- }
- interpreter.intPool.put(x, y)
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.SMod(&x, y)
return nil, nil
}
func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- base, exponent := callContext.stack.pop(), callContext.stack.pop()
- // some shortcuts
- cmpToOne := exponent.Cmp(big1)
- if cmpToOne < 0 { // Exponent is zero
- // x ^ 0 == 1
- callContext.stack.push(base.SetUint64(1))
- } else if base.Sign() == 0 {
- // 0 ^ y, if y != 0, == 0
- callContext.stack.push(base.SetUint64(0))
- } else if cmpToOne == 0 { // Exponent is one
- // x ^ 1 == x
- callContext.stack.push(base)
- } else {
- callContext.stack.push(math.Exp(base, exponent))
- interpreter.intPool.putOne(base)
- }
- interpreter.intPool.putOne(exponent)
+ base, exponent := callContext.stack.pop(), callContext.stack.peek()
+ exponent.Exp(&base, exponent)
return nil, nil
}
func opSignExtend(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- back := callContext.stack.pop()
- if back.Cmp(big.NewInt(31)) < 0 {
- bit := uint(back.Uint64()*8 + 7)
- num := callContext.stack.pop()
- mask := back.Lsh(common.Big1, bit)
- mask.Sub(mask, common.Big1)
- if num.Bit(int(bit)) > 0 {
- num.Or(num, mask.Not(mask))
- } else {
- num.And(num, mask)
- }
-
- callContext.stack.push(math.U256(num))
- }
-
- interpreter.intPool.putOne(back)
+ back, num := callContext.stack.pop(), callContext.stack.peek()
+ num.ExtendSign(num, &back)
return nil, nil
}
func opNot(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x := callContext.stack.peek()
- math.U256(x.Not(x))
+ x.Not(x)
return nil, nil
}
func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
- if x.Cmp(y) < 0 {
- y.SetUint64(1)
+ if x.Lt(y) {
+ y.SetOne()
} else {
- y.SetUint64(0)
+ y.Clear()
}
- interpreter.intPool.putOne(x)
return nil, nil
}
func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
- if x.Cmp(y) > 0 {
- y.SetUint64(1)
+ if x.Gt(y) {
+ y.SetOne()
} else {
- y.SetUint64(0)
+ y.Clear()
}
- interpreter.intPool.putOne(x)
return nil, nil
}
func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
-
- xSign := x.Cmp(tt255)
- ySign := y.Cmp(tt255)
-
- switch {
- case xSign >= 0 && ySign < 0:
- y.SetUint64(1)
-
- case xSign < 0 && ySign >= 0:
- y.SetUint64(0)
-
- default:
- if x.Cmp(y) < 0 {
- y.SetUint64(1)
- } else {
- y.SetUint64(0)
- }
+ if x.Slt(y) {
+ y.SetOne()
+ } else {
+ y.Clear()
}
- interpreter.intPool.putOne(x)
return nil, nil
}
func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
-
- xSign := x.Cmp(tt255)
- ySign := y.Cmp(tt255)
-
- switch {
- case xSign >= 0 && ySign < 0:
- y.SetUint64(0)
-
- case xSign < 0 && ySign >= 0:
- y.SetUint64(1)
-
- default:
- if x.Cmp(y) > 0 {
- y.SetUint64(1)
- } else {
- y.SetUint64(0)
- }
+ if x.Sgt(y) {
+ y.SetOne()
+ } else {
+ y.Clear()
}
- interpreter.intPool.putOne(x)
return nil, nil
}
func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
- if x.Cmp(y) == 0 {
- y.SetUint64(1)
+ if x.Eq(y) {
+ y.SetOne()
} else {
- y.SetUint64(0)
+ y.Clear()
}
- interpreter.intPool.putOne(x)
return nil, nil
}
func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x := callContext.stack.peek()
- if x.Sign() > 0 {
- x.SetUint64(0)
+ if x.IsZero() {
+ x.SetOne()
} else {
- x.SetUint64(1)
+ x.Clear()
}
return nil, nil
}
func opAnd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.pop()
- callContext.stack.push(x.And(x, y))
-
- interpreter.intPool.putOne(y)
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.And(&x, y)
return nil, nil
}
func opOr(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
- y.Or(x, y)
-
- interpreter.intPool.putOne(x)
+ y.Or(&x, y)
return nil, nil
}
func opXor(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
- y.Xor(x, y)
-
- interpreter.intPool.putOne(x)
+ y.Xor(&x, y)
return nil, nil
}
func opByte(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
th, val := callContext.stack.pop(), callContext.stack.peek()
- if th.Cmp(common.Big32) < 0 {
- b := math.Byte(val, 32, int(th.Int64()))
- val.SetUint64(uint64(b))
- } else {
- val.SetUint64(0)
- }
- interpreter.intPool.putOne(th)
+ val.Byte(&th)
return nil, nil
}
func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
- if z.Cmp(bigZero) > 0 {
- x.Add(x, y)
- x.Mod(x, z)
- callContext.stack.push(math.U256(x))
+ x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek()
+ if z.IsZero() {
+ z.Clear()
} else {
- callContext.stack.push(x.SetUint64(0))
+ z.AddMod(&x, &y, z)
}
- interpreter.intPool.put(y, z)
return nil, nil
}
func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
- if z.Cmp(bigZero) > 0 {
- x.Mul(x, y)
- x.Mod(x, z)
- callContext.stack.push(math.U256(x))
- } else {
- callContext.stack.push(x.SetUint64(0))
- }
- interpreter.intPool.put(y, z)
+ x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek()
+ z.MulMod(&x, &y, z)
return nil, nil
}
@@ -322,16 +191,12 @@ func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
// and pushes on the stack arg2 shifted to the left by arg1 number of bits.
func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
- shift, value := math.U256(callContext.stack.pop()), math.U256(callContext.stack.peek())
- defer interpreter.intPool.putOne(shift) // First operand back into the pool
-
- if shift.Cmp(common.Big256) >= 0 {
- value.SetUint64(0)
- return nil, nil
+ shift, value := callContext.stack.pop(), callContext.stack.peek()
+ if shift.LtUint64(256) {
+ value.Lsh(value, uint(shift.Uint64()))
+ } else {
+ value.Clear()
}
- n := uint(shift.Uint64())
- math.U256(value.Lsh(value, n))
-
return nil, nil
}
@@ -340,16 +205,12 @@ func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
// and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill.
func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
- shift, value := math.U256(callContext.stack.pop()), math.U256(callContext.stack.peek())
- defer interpreter.intPool.putOne(shift) // First operand back into the pool
-
- if shift.Cmp(common.Big256) >= 0 {
- value.SetUint64(0)
- return nil, nil
+ shift, value := callContext.stack.pop(), callContext.stack.peek()
+ if shift.LtUint64(256) {
+ value.Rsh(value, uint(shift.Uint64()))
+ } else {
+ value.Clear()
}
- n := uint(shift.Uint64())
- math.U256(value.Rsh(value, n))
-
return nil, nil
}
@@ -357,29 +218,24 @@ func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
// The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2,
// and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension.
func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- // Note, S256 returns (potentially) a new bigint, so we're popping, not peeking this one
- shift, value := math.U256(callContext.stack.pop()), math.S256(callContext.stack.pop())
- defer interpreter.intPool.putOne(shift) // First operand back into the pool
-
- if shift.Cmp(common.Big256) >= 0 {
+ shift, value := callContext.stack.pop(), callContext.stack.peek()
+ if shift.GtUint64(256) {
if value.Sign() >= 0 {
- value.SetUint64(0)
+ value.Clear()
} else {
- value.SetInt64(-1)
+ // Max negative shift: all bits set
+ value.SetAllOne()
}
- callContext.stack.push(math.U256(value))
return nil, nil
}
n := uint(shift.Uint64())
- value.Rsh(value, n)
- callContext.stack.push(math.U256(value))
-
+ value.SRsh(value, n)
return nil, nil
}
-func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- offset, size := callContext.stack.pop(), callContext.stack.pop()
- data := callContext.memory.GetPtr(offset.Int64(), size.Int64())
+func opKeccak256(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ offset, size := callContext.stack.pop(), callContext.stack.peek()
+ data := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
if interpreter.hasher == nil {
interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState)
@@ -393,45 +249,50 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
if evm.vmConfig.EnablePreimageRecording {
evm.StateDB.AddPreimage(interpreter.hasherBuf, data)
}
- callContext.stack.push(interpreter.intPool.get().SetBytes(interpreter.hasherBuf[:]))
- interpreter.intPool.put(offset, size)
+ size.SetBytes(interpreter.hasherBuf[:])
return nil, nil
}
-
func opAddress(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetBytes(callContext.contract.Address().Bytes()))
+ callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Address().Bytes()))
return nil, nil
}
func opBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
slot := callContext.stack.peek()
- slot.Set(interpreter.evm.StateDB.GetBalance(common.BigToAddress(slot)))
+ address := common.Address(slot.Bytes20())
+ slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address))
return nil, nil
}
func opOrigin(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Origin.Bytes()))
+ callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes()))
return nil, nil
}
-
func opCaller(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetBytes(callContext.contract.Caller().Bytes()))
+ callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Caller().Bytes()))
return nil, nil
}
func opCallValue(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().Set(callContext.contract.value))
+ v, _ := uint256.FromBig(callContext.contract.value)
+ callContext.stack.push(v)
return nil, nil
}
func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetBytes(getDataBig(callContext.contract.Input, callContext.stack.pop(), big32)))
+ x := callContext.stack.peek()
+ if offset, overflow := x.Uint64WithOverflow(); !overflow {
+ data := getData(callContext.contract.Input, offset, 32)
+ x.SetBytes(data)
+ } else {
+ x.Clear()
+ }
return nil, nil
}
func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetInt64(int64(len(callContext.contract.Input))))
+ callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(callContext.contract.Input))))
return nil, nil
}
@@ -441,14 +302,20 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
dataOffset = callContext.stack.pop()
length = callContext.stack.pop()
)
- callContext.memory.Set(memOffset.Uint64(), length.Uint64(), getDataBig(callContext.contract.Input, dataOffset, length))
+ dataOffset64, overflow := dataOffset.Uint64WithOverflow()
+ if overflow {
+ dataOffset64 = 0xffffffffffffffff
+ }
+ // These values are checked for overflow during gas cost calculation
+ memOffset64 := memOffset.Uint64()
+ length64 := length.Uint64()
+ callContext.memory.Set(memOffset64, length64, getData(callContext.contract.Input, dataOffset64, length64))
- interpreter.intPool.put(memOffset, dataOffset, length)
return nil, nil
}
func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetUint64(uint64(len(interpreter.returnData))))
+ callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData))))
return nil, nil
}
@@ -457,30 +324,33 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *call
memOffset = callContext.stack.pop()
dataOffset = callContext.stack.pop()
length = callContext.stack.pop()
-
- end = interpreter.intPool.get().Add(dataOffset, length)
)
- defer interpreter.intPool.put(memOffset, dataOffset, length, end)
- if !end.IsUint64() || uint64(len(interpreter.returnData)) < end.Uint64() {
+ offset64, overflow := dataOffset.Uint64WithOverflow()
+ if overflow {
return nil, ErrReturnDataOutOfBounds
}
- callContext.memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()])
-
+ // we can reuse dataOffset now (aliasing it for clarity)
+ var end = dataOffset
+ end.Add(&dataOffset, &length)
+ end64, overflow := end.Uint64WithOverflow()
+ if overflow || uint64(len(interpreter.returnData)) < end64 {
+ return nil, ErrReturnDataOutOfBounds
+ }
+ callContext.memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[offset64:end64])
return nil, nil
}
func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
slot := callContext.stack.peek()
- slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.BigToAddress(slot))))
-
+ slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.Address(slot.Bytes20()))))
return nil, nil
}
func opCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- l := interpreter.intPool.get().SetInt64(int64(len(callContext.contract.Code)))
+ l := new(uint256.Int)
+ l.SetUint64(uint64(len(callContext.contract.Code)))
callContext.stack.push(l)
-
return nil, nil
}
@@ -490,24 +360,32 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
codeOffset = callContext.stack.pop()
length = callContext.stack.pop()
)
- codeCopy := getDataBig(callContext.contract.Code, codeOffset, length)
+ uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
+ if overflow {
+ uint64CodeOffset = 0xffffffffffffffff
+ }
+ codeCopy := getData(callContext.contract.Code, uint64CodeOffset, length.Uint64())
callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
- interpreter.intPool.put(memOffset, codeOffset, length)
return nil, nil
}
func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
- addr = common.BigToAddress(callContext.stack.pop())
- memOffset = callContext.stack.pop()
- codeOffset = callContext.stack.pop()
- length = callContext.stack.pop()
+ stack = callContext.stack
+ a = stack.pop()
+ memOffset = stack.pop()
+ codeOffset = stack.pop()
+ length = stack.pop()
)
- codeCopy := getDataBig(interpreter.evm.StateDB.GetCode(addr), codeOffset, length)
+ uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
+ if overflow {
+ uint64CodeOffset = 0xffffffffffffffff
+ }
+ addr := common.Address(a.Bytes20())
+ codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64())
callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
- interpreter.intPool.put(memOffset, codeOffset, length)
return nil, nil
}
@@ -539,9 +417,9 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
// this account should be regarded as a non-existent account and zero should be returned.
func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
slot := callContext.stack.peek()
- address := common.BigToAddress(slot)
+ address := common.Address(slot.Bytes20())
if interpreter.evm.StateDB.Empty(address) {
- slot.SetUint64(0)
+ slot.Clear()
} else {
slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes())
}
@@ -549,56 +427,80 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
}
func opGasprice(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().Set(interpreter.evm.GasPrice))
+ v, _ := uint256.FromBig(interpreter.evm.GasPrice)
+ callContext.stack.push(v)
return nil, nil
}
func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- num := callContext.stack.pop()
-
- n := interpreter.intPool.get().Sub(interpreter.evm.BlockNumber, common.Big257)
- if num.Cmp(n) > 0 && num.Cmp(interpreter.evm.BlockNumber) < 0 {
- callContext.stack.push(interpreter.evm.GetHash(num.Uint64()).Big())
+ num := callContext.stack.peek()
+ num64, overflow := num.Uint64WithOverflow()
+ if overflow {
+ num.Clear()
+ return nil, nil
+ }
+ var upper, lower uint64
+ upper = interpreter.evm.BlockNumber.Uint64()
+ if upper < 257 {
+ lower = 0
+ } else {
+ lower = upper - 256
+ }
+ if num64 >= lower && num64 < upper {
+ num.SetBytes(interpreter.evm.GetHash(num64).Bytes())
} else {
- callContext.stack.push(interpreter.intPool.getZero())
+ num.Clear()
}
- interpreter.intPool.put(num, n)
return nil, nil
}
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Coinbase.Bytes()))
+ callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Coinbase.Bytes()))
return nil, nil
}
func opTimestamp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.Time)))
+ v, _ := uint256.FromBig(interpreter.evm.Time)
+ callContext.stack.push(v)
return nil, nil
}
func opNumber(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.BlockNumber)))
+ v, _ := uint256.FromBig(interpreter.evm.BlockNumber)
+ callContext.stack.push(v)
return nil, nil
}
func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.Difficulty)))
+ v, _ := uint256.FromBig(interpreter.evm.Difficulty)
+ callContext.stack.push(v)
+ return nil, nil
+}
+
+func opRandom(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ var v *uint256.Int
+ if interpreter.evm.Context.Random != nil {
+ v = new(uint256.Int).SetBytes((interpreter.evm.Context.Random.Bytes()))
+ } else { // if context random is not set, use emptyCodeHash as default
+ v = new(uint256.Int).SetBytes(emptyCodeHash.Bytes())
+ }
+ callContext.stack.push(v)
return nil, nil
}
func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(math.U256(interpreter.intPool.get().SetUint64(interpreter.evm.GasLimit)))
+ callContext.stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit))
return nil, nil
}
func opPop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- interpreter.intPool.putOne(callContext.stack.pop())
+ callContext.stack.pop()
return nil, nil
}
func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
v := callContext.stack.peek()
- offset := v.Int64()
+ offset := int64(v.Uint64())
v.SetBytes(callContext.memory.GetPtr(offset, 32))
return nil, nil
}
@@ -606,58 +508,58 @@ func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]b
func opMstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// pop value of the stack
mStart, val := callContext.stack.pop(), callContext.stack.pop()
- callContext.memory.Set32(mStart.Uint64(), val)
-
- interpreter.intPool.put(mStart, val)
+ callContext.memory.Set32(mStart.Uint64(), &val)
return nil, nil
}
func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- off, val := callContext.stack.pop().Int64(), callContext.stack.pop().Int64()
- callContext.memory.store[off] = byte(val & 0xff)
-
+ off, val := callContext.stack.pop(), callContext.stack.pop()
+ callContext.memory.store[off.Uint64()] = byte(val.Uint64())
return nil, nil
}
func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
loc := callContext.stack.peek()
- val := interpreter.evm.StateDB.GetState(callContext.contract.Address(), common.BigToHash(loc))
+ hash := common.Hash(loc.Bytes32())
+ val := interpreter.evm.StateDB.GetState(callContext.contract.Address(), hash)
loc.SetBytes(val.Bytes())
return nil, nil
}
func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- loc := common.BigToHash(callContext.stack.pop())
+ if interpreter.readOnly {
+ return nil, ErrWriteProtection
+ }
+ loc := callContext.stack.pop()
val := callContext.stack.pop()
- interpreter.evm.StateDB.SetState(callContext.contract.Address(), loc, common.BigToHash(val))
-
- interpreter.intPool.putOne(val)
+ interpreter.evm.StateDB.SetState(callContext.contract.Address(),
+ common.Hash(loc.Bytes32()), common.Hash(val.Bytes32()))
return nil, nil
}
func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ if atomic.LoadInt32(&interpreter.evm.abort) != 0 {
+ return nil, errStopToken
+ }
pos := callContext.stack.pop()
- if !callContext.contract.validJumpdest(pos) {
+ if !callContext.contract.validJumpdest(&pos) {
return nil, ErrInvalidJump
}
- *pc = pos.Uint64()
-
- interpreter.intPool.putOne(pos)
+ *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop
return nil, nil
}
func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ if atomic.LoadInt32(&interpreter.evm.abort) != 0 {
+ return nil, errStopToken
+ }
pos, cond := callContext.stack.pop(), callContext.stack.pop()
- if cond.Sign() != 0 {
- if !callContext.contract.validJumpdest(pos) {
+ if !cond.IsZero() {
+ if !callContext.contract.validJumpdest(&pos) {
return nil, ErrInvalidJump
}
- *pc = pos.Uint64()
- } else {
- *pc++
+ *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop
}
-
- interpreter.intPool.put(pos, cond)
return nil, nil
}
@@ -666,219 +568,253 @@ func opJumpdest(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
}
func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetUint64(*pc))
+ callContext.stack.push(new(uint256.Int).SetUint64(*pc))
return nil, nil
}
func opMsize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetInt64(int64(callContext.memory.Len())))
+ callContext.stack.push(new(uint256.Int).SetUint64(uint64(callContext.memory.Len())))
return nil, nil
}
func opGas(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetUint64(callContext.contract.Gas))
+ callContext.stack.push(new(uint256.Int).SetUint64(callContext.contract.Gas))
return nil, nil
}
func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ if interpreter.readOnly {
+ return nil, ErrWriteProtection
+ }
var (
value = callContext.stack.pop()
offset, size = callContext.stack.pop(), callContext.stack.pop()
- input = callContext.memory.GetCopy(offset.Int64(), size.Int64())
+ input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
gas = callContext.contract.Gas
)
if interpreter.evm.chainRules.IsEIP150 {
gas -= gas / 64
}
+ // reuse size int for stackvalue
+ stackvalue := size
callContext.contract.UseGas(gas)
- res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, value)
+ res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, value.ToBig())
// Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must
// ignore this error and pretend the operation was successful.
if interpreter.evm.chainRules.IsHomestead && suberr == ErrCodeStoreOutOfGas {
- callContext.stack.push(interpreter.intPool.getZero())
+ stackvalue.Clear()
} else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
- callContext.stack.push(interpreter.intPool.getZero())
+ stackvalue.Clear()
} else {
- callContext.stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
+ stackvalue.SetBytes(addr.Bytes())
}
+ callContext.stack.push(&stackvalue)
callContext.contract.Gas += returnGas
- interpreter.intPool.put(value, offset, size)
if suberr == ErrExecutionReverted {
+ interpreter.returnData = res // set REVERT data to return data buffer
return res, nil
}
+ interpreter.returnData = nil // clear dirty return data buffer
return nil, nil
}
func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ if interpreter.readOnly {
+ return nil, ErrWriteProtection
+ }
var (
endowment = callContext.stack.pop()
offset, size = callContext.stack.pop(), callContext.stack.pop()
salt = callContext.stack.pop()
- input = callContext.memory.GetCopy(offset.Int64(), size.Int64())
+ input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
gas = callContext.contract.Gas
)
// Apply EIP150
gas -= gas / 64
callContext.contract.UseGas(gas)
- res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas, endowment, salt)
+ // reuse size int for stackvalue
+ stackvalue := size
+ res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas,
+ endowment.ToBig(), salt.ToBig())
// Push item on the stack based on the returned error.
if suberr != nil {
- callContext.stack.push(interpreter.intPool.getZero())
+ stackvalue.Clear()
} else {
- callContext.stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
+ stackvalue.SetBytes(addr.Bytes())
}
+ callContext.stack.push(&stackvalue)
callContext.contract.Gas += returnGas
- interpreter.intPool.put(endowment, offset, size, salt)
if suberr == ErrExecutionReverted {
+ interpreter.returnData = res // set REVERT data to return data buffer
return res, nil
}
+ interpreter.returnData = nil // clear dirty return data buffer
return nil, nil
}
func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ stack := callContext.stack
// Pop gas. The actual gas in interpreter.evm.callGasTemp.
- interpreter.intPool.putOne(callContext.stack.pop())
+ // We can use this as a temporary value
+ temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
- addr, value, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
- toAddr := common.BigToAddress(addr)
- value = math.U256(value)
+ addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ toAddr := common.Address(addr.Bytes20())
// Get the arguments from the memory.
- args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64())
+ args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
- if value.Sign() != 0 {
+ if interpreter.readOnly && !value.IsZero() {
+ return nil, ErrWriteProtection
+ }
+ if !value.IsZero() {
gas += params.CallStipend
}
- ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, value)
+ ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, value.ToBig())
if err != nil {
- callContext.stack.push(interpreter.intPool.getZero())
+ temp.Clear()
} else {
- callContext.stack.push(interpreter.intPool.get().SetUint64(1))
+ temp.SetOne()
}
+ stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
ret = common.CopyBytes(ret)
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
callContext.contract.Gas += returnGas
- interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize)
+ interpreter.returnData = ret
return ret, nil
}
func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
- interpreter.intPool.putOne(callContext.stack.pop())
+ stack := callContext.stack
+ // We use it as a temporary value
+ temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
- addr, value, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
- toAddr := common.BigToAddress(addr)
- value = math.U256(value)
+ addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
- args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64())
+ args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
- if value.Sign() != 0 {
+ if !value.IsZero() {
gas += params.CallStipend
}
- ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, value)
+ ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, value.ToBig())
if err != nil {
- callContext.stack.push(interpreter.intPool.getZero())
+ temp.Clear()
} else {
- callContext.stack.push(interpreter.intPool.get().SetUint64(1))
+ temp.SetOne()
}
+ stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
ret = common.CopyBytes(ret)
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
callContext.contract.Gas += returnGas
- interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize)
+ interpreter.returnData = ret
return ret, nil
}
func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ stack := callContext.stack
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
- interpreter.intPool.putOne(callContext.stack.pop())
+ // We use it as a temporary value
+ temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
- addr, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
- toAddr := common.BigToAddress(addr)
+ addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
- args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64())
+ args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
ret, returnGas, err := interpreter.evm.DelegateCall(callContext.contract, toAddr, args, gas)
if err != nil {
- callContext.stack.push(interpreter.intPool.getZero())
+ temp.Clear()
} else {
- callContext.stack.push(interpreter.intPool.get().SetUint64(1))
+ temp.SetOne()
}
+ stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
ret = common.CopyBytes(ret)
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
callContext.contract.Gas += returnGas
- interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize)
+ interpreter.returnData = ret
return ret, nil
}
func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
- interpreter.intPool.putOne(callContext.stack.pop())
+ stack := callContext.stack
+ // We use it as a temporary value
+ temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
- addr, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
- toAddr := common.BigToAddress(addr)
+ addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
- args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64())
+ args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
ret, returnGas, err := interpreter.evm.StaticCall(callContext.contract, toAddr, args, gas)
if err != nil {
- callContext.stack.push(interpreter.intPool.getZero())
+ temp.Clear()
} else {
- callContext.stack.push(interpreter.intPool.get().SetUint64(1))
+ temp.SetOne()
}
+ stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
ret = common.CopyBytes(ret)
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
callContext.contract.Gas += returnGas
- interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize)
+ interpreter.returnData = ret
return ret, nil
}
func opReturn(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
offset, size := callContext.stack.pop(), callContext.stack.pop()
- ret := callContext.memory.GetPtr(offset.Int64(), size.Int64())
+ ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
- interpreter.intPool.put(offset, size)
- return ret, nil
+ return ret, errStopToken
}
func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
offset, size := callContext.stack.pop(), callContext.stack.pop()
- ret := callContext.memory.GetPtr(offset.Int64(), size.Int64())
+ ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
- interpreter.intPool.put(offset, size)
- return ret, nil
+ interpreter.returnData = ret
+ return ret, ErrExecutionReverted
+}
+
+func opUndefined(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ return nil, &ErrInvalidOpCode{opcode: OpCode(callContext.contract.Code[*pc])}
}
func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- return nil, nil
+ return nil, errStopToken
}
-func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ if interpreter.readOnly {
+ return nil, ErrWriteProtection
+ }
+ beneficiary := callContext.stack.pop()
balance := interpreter.evm.StateDB.GetBalance(callContext.contract.Address())
- interpreter.evm.StateDB.AddBalance(common.BigToAddress(callContext.stack.pop()), balance)
-
+ interpreter.evm.StateDB.AddBalance(common.Address(beneficiary.Bytes20()), balance)
interpreter.evm.StateDB.Suicide(callContext.contract.Address())
- return nil, nil
+ return nil, errStopToken
}
// following functions are used by the instruction jump table
@@ -886,13 +822,18 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
// make log instruction function
func makeLog(size int) executionFunc {
return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ if interpreter.readOnly {
+ return nil, ErrWriteProtection
+ }
topics := make([]common.Hash, size)
- mStart, mSize := callContext.stack.pop(), callContext.stack.pop()
+ stack := callContext.stack
+ mStart, mSize := stack.pop(), stack.pop()
for i := 0; i < size; i++ {
- topics[i] = common.BigToHash(callContext.stack.pop())
+ addr := stack.pop()
+ topics[i] = common.Hash(addr.Bytes32())
}
- d := callContext.memory.GetCopy(mStart.Int64(), mSize.Int64())
+ d := callContext.memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64()))
interpreter.evm.StateDB.AddLog(&types.Log{
Address: callContext.contract.Address(),
Topics: topics,
@@ -902,7 +843,6 @@ func makeLog(size int) executionFunc {
BlockNumber: interpreter.evm.BlockNumber.Uint64(),
})
- interpreter.intPool.put(mStart, mSize)
return nil, nil
}
}
@@ -911,13 +851,13 @@ func makeLog(size int) executionFunc {
func opPush1(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
codeLen = uint64(len(callContext.contract.Code))
- integer = interpreter.intPool.get()
+ integer = new(uint256.Int)
)
*pc += 1
if *pc < codeLen {
callContext.stack.push(integer.SetUint64(uint64(callContext.contract.Code[*pc])))
} else {
- callContext.stack.push(integer.SetUint64(0))
+ callContext.stack.push(integer.Clear())
}
return nil, nil
}
@@ -937,8 +877,9 @@ func makePush(size uint64, pushByteSize int) executionFunc {
endMin = startMin + pushByteSize
}
- integer := interpreter.intPool.get()
- callContext.stack.push(integer.SetBytes(common.RightPadBytes(callContext.contract.Code[startMin:endMin], pushByteSize)))
+ integer := new(uint256.Int)
+ callContext.stack.push(integer.SetBytes(common.RightPadBytes(
+ callContext.contract.Code[startMin:endMin], pushByteSize)))
*pc += size
return nil, nil
@@ -948,7 +889,7 @@ func makePush(size uint64, pushByteSize int) executionFunc {
// make dup instruction function
func makeDup(size int64) executionFunc {
return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.dup(interpreter.intPool, int(size))
+ callContext.stack.dup(int(size))
return nil, nil
}
}
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go
index f439b643a5dd..200aa1477235 100644
--- a/core/vm/instructions_test.go
+++ b/core/vm/instructions_test.go
@@ -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)
+ }
}
}
diff --git a/core/vm/int_pool_verifier.go b/core/vm/int_pool_verifier.go
deleted file mode 100644
index 82fbfed699de..000000000000
--- a/core/vm/int_pool_verifier.go
+++ /dev/null
@@ -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 .
-
-// +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))
- }
- }
-}
diff --git a/core/vm/int_pool_verifier_empty.go b/core/vm/int_pool_verifier_empty.go
deleted file mode 100644
index a5f1dc02b7fe..000000000000
--- a/core/vm/int_pool_verifier_empty.go
+++ /dev/null
@@ -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 .
-
-// +build !VERIFY_EVM_INTEGER_POOL
-
-package vm
-
-const verifyPool = false
-
-func verifyIntegerPool(ip *intPool) {}
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 2662a1922ec8..23cc5a03fe3d 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -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++
+ }
+
+ if err == errStopToken {
+ err = nil // clear stop token error
}
- return nil, nil
+
+ return res, err
}
// CanRun tells if the contract, passed as an argument, can be
diff --git a/core/vm/intpool.go b/core/vm/intpool.go
deleted file mode 100644
index eed074b07338..000000000000
--- a/core/vm/intpool.go
+++ /dev/null
@@ -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 .
-
-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)
- }
-}
diff --git a/core/vm/intpool_test.go b/core/vm/intpool_test.go
deleted file mode 100644
index 6c0d00f3ce5c..000000000000
--- a/core/vm/intpool_test.go
+++ /dev/null
@@ -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 .
-
-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)
- }
-}
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index b3a0f7ff7ce9..2302a439df6e 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -40,13 +40,6 @@ type operation struct {
// memorySize returns the memory size required for the operation
memorySize memorySizeFunc
-
- halts bool // indicates whether the operation should halt further execution
- jumps bool // indicates whether the program counter should not increment
- writes bool // determines whether this a state modifying operation
- valid bool // indication whether the retrieved operation is valid and known
- reverts bool // determines whether the operation reverts state (implicitly halts)
- returns bool // determines whether the operations sets the return data content
}
var (
@@ -57,10 +50,48 @@ var (
byzantiumInstructionSet = newByzantiumInstructionSet()
constantinopleInstructionSet = newConstantinopleInstructionSet()
istanbulInstructionSet = newIstanbulInstructionSet()
+ berlinInstructionSet = newBerlinInstructionSet()
+ londonInstructionSet = newLondonInstructionSet()
+ mergeInstructionSet = newMergeInstructionSet()
+ shanghaiInstructionSet = newShanghaiInstructionSet()
)
// JumpTable contains the EVM opcodes supported at a given fork.
-type JumpTable [256]operation
+type JumpTable [256]*operation
+
+func newShanghaiInstructionSet() JumpTable {
+ instructionSet := newMergeInstructionSet()
+ enable3855(&instructionSet) // PUSH0 instruction
+ return instructionSet
+}
+
+func newMergeInstructionSet() JumpTable {
+ instructionSet := newLondonInstructionSet()
+ instructionSet[PREVRANDAO] = &operation{
+ execute: opRandom,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ }
+ return instructionSet
+}
+
+// newLondonInstructionSet returns the frontier, homestead, byzantium,
+// constantinople, istanbul, petersburg, berlin and london instructions.
+func newLondonInstructionSet() JumpTable {
+ instructionSet := newBerlinInstructionSet()
+ // enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
+ enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
+ return instructionSet
+}
+
+// newBerlinInstructionSet returns the frontier, homestead, byzantium,
+// constantinople, istanbul, petersburg and berlin instructions.
+func newBerlinInstructionSet() JumpTable {
+ instructionSet := newIstanbulInstructionSet()
+ // enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929
+ return instructionSet
+}
// newIstanbulInstructionSet returns the frontier, homestead
// byzantium, contantinople and petersburg instructions.
@@ -78,44 +109,37 @@ func newIstanbulInstructionSet() JumpTable {
// byzantium and contantinople instructions.
func newConstantinopleInstructionSet() JumpTable {
instructionSet := newByzantiumInstructionSet()
- instructionSet[SHL] = operation{
+ instructionSet[SHL] = &operation{
execute: opSHL,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
}
- instructionSet[SHR] = operation{
+ instructionSet[SHR] = &operation{
execute: opSHR,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
}
- instructionSet[SAR] = operation{
+ instructionSet[SAR] = &operation{
execute: opSAR,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
}
- instructionSet[EXTCODEHASH] = operation{
+ instructionSet[EXTCODEHASH] = &operation{
execute: opExtCodeHash,
constantGas: params.ExtcodeHashGasConstantinople,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
}
- instructionSet[CREATE2] = operation{
+ instructionSet[CREATE2] = &operation{
execute: opCreate2,
constantGas: params.Create2Gas,
dynamicGas: gasCreate2,
minStack: minStack(4, 1),
maxStack: maxStack(4, 1),
memorySize: memoryCreate2,
- valid: true,
- writes: true,
- returns: true,
}
return instructionSet
}
@@ -124,41 +148,34 @@ func newConstantinopleInstructionSet() JumpTable {
// byzantium instructions.
func newByzantiumInstructionSet() JumpTable {
instructionSet := newSpuriousDragonInstructionSet()
- instructionSet[STATICCALL] = operation{
+ instructionSet[STATICCALL] = &operation{
execute: opStaticCall,
constantGas: params.CallGasEIP150,
dynamicGas: gasStaticCall,
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
memorySize: memoryStaticCall,
- valid: true,
- returns: true,
}
- instructionSet[RETURNDATASIZE] = operation{
+ instructionSet[RETURNDATASIZE] = &operation{
execute: opReturnDataSize,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
}
- instructionSet[RETURNDATACOPY] = operation{
+ instructionSet[RETURNDATACOPY] = &operation{
execute: opReturnDataCopy,
constantGas: GasFastestStep,
dynamicGas: gasReturnDataCopy,
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryReturnDataCopy,
- valid: true,
}
- instructionSet[REVERT] = operation{
+ instructionSet[REVERT] = &operation{
execute: opRevert,
dynamicGas: gasRevert,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryRevert,
- valid: true,
- reverts: true,
- returns: true,
}
return instructionSet
}
@@ -188,15 +205,13 @@ func newTangerineWhistleInstructionSet() JumpTable {
// instructions that can be executed during the homestead phase.
func newHomesteadInstructionSet() JumpTable {
instructionSet := newFrontierInstructionSet()
- instructionSet[DELEGATECALL] = operation{
+ instructionSet[DELEGATECALL] = &operation{
execute: opDelegateCall,
dynamicGas: gasDelegateCall,
constantGas: params.CallGasFrontier,
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
memorySize: memoryDelegateCall,
- valid: true,
- returns: true,
}
return instructionSet
}
@@ -204,226 +219,194 @@ func newHomesteadInstructionSet() JumpTable {
// newFrontierInstructionSet returns the frontier instructions
// that can be executed during the frontier phase.
func newFrontierInstructionSet() JumpTable {
- return JumpTable{
+ tbl := JumpTable{
STOP: {
execute: opStop,
constantGas: 0,
minStack: minStack(0, 0),
maxStack: maxStack(0, 0),
- halts: true,
- valid: true,
},
ADD: {
execute: opAdd,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
MUL: {
execute: opMul,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SUB: {
execute: opSub,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
DIV: {
execute: opDiv,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SDIV: {
execute: opSdiv,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
MOD: {
execute: opMod,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SMOD: {
execute: opSmod,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
ADDMOD: {
execute: opAddmod,
constantGas: GasMidStep,
minStack: minStack(3, 1),
maxStack: maxStack(3, 1),
- valid: true,
},
MULMOD: {
execute: opMulmod,
constantGas: GasMidStep,
minStack: minStack(3, 1),
maxStack: maxStack(3, 1),
- valid: true,
},
EXP: {
execute: opExp,
dynamicGas: gasExpFrontier,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SIGNEXTEND: {
execute: opSignExtend,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
LT: {
execute: opLt,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
GT: {
execute: opGt,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SLT: {
execute: opSlt,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SGT: {
execute: opSgt,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
EQ: {
execute: opEq,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
ISZERO: {
execute: opIszero,
constantGas: GasFastestStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
AND: {
execute: opAnd,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
XOR: {
execute: opXor,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
OR: {
execute: opOr,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
NOT: {
execute: opNot,
constantGas: GasFastestStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
BYTE: {
execute: opByte,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
- SHA3: {
- execute: opSha3,
- constantGas: params.Sha3Gas,
- dynamicGas: gasSha3,
+ KECCAK256: {
+ execute: opKeccak256,
+ constantGas: params.Keccak256Gas,
+ dynamicGas: gasKeccak256,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- memorySize: memorySha3,
- valid: true,
+ memorySize: memoryKeccak256,
},
ADDRESS: {
execute: opAddress,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
BALANCE: {
execute: opBalance,
constantGas: params.BalanceGasFrontier,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
ORIGIN: {
execute: opOrigin,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
CALLER: {
execute: opCaller,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
CALLVALUE: {
execute: opCallValue,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
CALLDATALOAD: {
execute: opCallDataLoad,
constantGas: GasFastestStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
CALLDATASIZE: {
execute: opCallDataSize,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
CALLDATACOPY: {
execute: opCallDataCopy,
@@ -432,14 +415,12 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryCallDataCopy,
- valid: true,
},
CODESIZE: {
execute: opCodeSize,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
CODECOPY: {
execute: opCodeCopy,
@@ -448,21 +429,18 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryCodeCopy,
- valid: true,
},
GASPRICE: {
execute: opGasprice,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
EXTCODESIZE: {
execute: opExtCodeSize,
constantGas: params.ExtcodeSizeGasFrontier,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
EXTCODECOPY: {
execute: opExtCodeCopy,
@@ -471,56 +449,48 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(4, 0),
maxStack: maxStack(4, 0),
memorySize: memoryExtCodeCopy,
- valid: true,
},
BLOCKHASH: {
execute: opBlockhash,
constantGas: GasExtStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
COINBASE: {
execute: opCoinbase,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
TIMESTAMP: {
execute: opTimestamp,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
NUMBER: {
execute: opNumber,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
DIFFICULTY: {
execute: opDifficulty,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
GASLIMIT: {
execute: opGasLimit,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
POP: {
execute: opPop,
constantGas: GasQuickStep,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
- valid: true,
},
MLOAD: {
execute: opMload,
@@ -529,7 +499,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
memorySize: memoryMLoad,
- valid: true,
},
MSTORE: {
execute: opMstore,
@@ -538,7 +507,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryMStore,
- valid: true,
},
MSTORE8: {
execute: opMstore8,
@@ -547,515 +515,438 @@ func newFrontierInstructionSet() JumpTable {
memorySize: memoryMStore8,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
-
- valid: true,
},
SLOAD: {
execute: opSload,
constantGas: params.SloadGasFrontier,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
SSTORE: {
execute: opSstore,
dynamicGas: gasSStore,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
- valid: true,
- writes: true,
},
JUMP: {
execute: opJump,
constantGas: GasMidStep,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
- jumps: true,
- valid: true,
},
JUMPI: {
execute: opJumpi,
constantGas: GasSlowStep,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
- jumps: true,
- valid: true,
},
PC: {
execute: opPc,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
MSIZE: {
execute: opMsize,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
GAS: {
execute: opGas,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
JUMPDEST: {
execute: opJumpdest,
constantGas: params.JumpdestGas,
minStack: minStack(0, 0),
maxStack: maxStack(0, 0),
- valid: true,
},
PUSH1: {
execute: opPush1,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH2: {
execute: makePush(2, 2),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH3: {
execute: makePush(3, 3),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH4: {
execute: makePush(4, 4),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH5: {
execute: makePush(5, 5),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH6: {
execute: makePush(6, 6),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH7: {
execute: makePush(7, 7),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH8: {
execute: makePush(8, 8),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH9: {
execute: makePush(9, 9),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH10: {
execute: makePush(10, 10),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH11: {
execute: makePush(11, 11),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH12: {
execute: makePush(12, 12),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH13: {
execute: makePush(13, 13),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH14: {
execute: makePush(14, 14),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH15: {
execute: makePush(15, 15),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH16: {
execute: makePush(16, 16),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH17: {
execute: makePush(17, 17),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH18: {
execute: makePush(18, 18),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH19: {
execute: makePush(19, 19),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH20: {
execute: makePush(20, 20),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH21: {
execute: makePush(21, 21),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH22: {
execute: makePush(22, 22),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH23: {
execute: makePush(23, 23),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH24: {
execute: makePush(24, 24),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH25: {
execute: makePush(25, 25),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH26: {
execute: makePush(26, 26),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH27: {
execute: makePush(27, 27),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH28: {
execute: makePush(28, 28),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH29: {
execute: makePush(29, 29),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH30: {
execute: makePush(30, 30),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH31: {
execute: makePush(31, 31),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH32: {
execute: makePush(32, 32),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
DUP1: {
execute: makeDup(1),
constantGas: GasFastestStep,
minStack: minDupStack(1),
maxStack: maxDupStack(1),
- valid: true,
},
DUP2: {
execute: makeDup(2),
constantGas: GasFastestStep,
minStack: minDupStack(2),
maxStack: maxDupStack(2),
- valid: true,
},
DUP3: {
execute: makeDup(3),
constantGas: GasFastestStep,
minStack: minDupStack(3),
maxStack: maxDupStack(3),
- valid: true,
},
DUP4: {
execute: makeDup(4),
constantGas: GasFastestStep,
minStack: minDupStack(4),
maxStack: maxDupStack(4),
- valid: true,
},
DUP5: {
execute: makeDup(5),
constantGas: GasFastestStep,
minStack: minDupStack(5),
maxStack: maxDupStack(5),
- valid: true,
},
DUP6: {
execute: makeDup(6),
constantGas: GasFastestStep,
minStack: minDupStack(6),
maxStack: maxDupStack(6),
- valid: true,
},
DUP7: {
execute: makeDup(7),
constantGas: GasFastestStep,
minStack: minDupStack(7),
maxStack: maxDupStack(7),
- valid: true,
},
DUP8: {
execute: makeDup(8),
constantGas: GasFastestStep,
minStack: minDupStack(8),
maxStack: maxDupStack(8),
- valid: true,
},
DUP9: {
execute: makeDup(9),
constantGas: GasFastestStep,
minStack: minDupStack(9),
maxStack: maxDupStack(9),
- valid: true,
},
DUP10: {
execute: makeDup(10),
constantGas: GasFastestStep,
minStack: minDupStack(10),
maxStack: maxDupStack(10),
- valid: true,
},
DUP11: {
execute: makeDup(11),
constantGas: GasFastestStep,
minStack: minDupStack(11),
maxStack: maxDupStack(11),
- valid: true,
},
DUP12: {
execute: makeDup(12),
constantGas: GasFastestStep,
minStack: minDupStack(12),
maxStack: maxDupStack(12),
- valid: true,
},
DUP13: {
execute: makeDup(13),
constantGas: GasFastestStep,
minStack: minDupStack(13),
maxStack: maxDupStack(13),
- valid: true,
},
DUP14: {
execute: makeDup(14),
constantGas: GasFastestStep,
minStack: minDupStack(14),
maxStack: maxDupStack(14),
- valid: true,
},
DUP15: {
execute: makeDup(15),
constantGas: GasFastestStep,
minStack: minDupStack(15),
maxStack: maxDupStack(15),
- valid: true,
},
DUP16: {
execute: makeDup(16),
constantGas: GasFastestStep,
minStack: minDupStack(16),
maxStack: maxDupStack(16),
- valid: true,
},
SWAP1: {
execute: makeSwap(1),
constantGas: GasFastestStep,
minStack: minSwapStack(2),
maxStack: maxSwapStack(2),
- valid: true,
},
SWAP2: {
execute: makeSwap(2),
constantGas: GasFastestStep,
minStack: minSwapStack(3),
maxStack: maxSwapStack(3),
- valid: true,
},
SWAP3: {
execute: makeSwap(3),
constantGas: GasFastestStep,
minStack: minSwapStack(4),
maxStack: maxSwapStack(4),
- valid: true,
},
SWAP4: {
execute: makeSwap(4),
constantGas: GasFastestStep,
minStack: minSwapStack(5),
maxStack: maxSwapStack(5),
- valid: true,
},
SWAP5: {
execute: makeSwap(5),
constantGas: GasFastestStep,
minStack: minSwapStack(6),
maxStack: maxSwapStack(6),
- valid: true,
},
SWAP6: {
execute: makeSwap(6),
constantGas: GasFastestStep,
minStack: minSwapStack(7),
maxStack: maxSwapStack(7),
- valid: true,
},
SWAP7: {
execute: makeSwap(7),
constantGas: GasFastestStep,
minStack: minSwapStack(8),
maxStack: maxSwapStack(8),
- valid: true,
},
SWAP8: {
execute: makeSwap(8),
constantGas: GasFastestStep,
minStack: minSwapStack(9),
maxStack: maxSwapStack(9),
- valid: true,
},
SWAP9: {
execute: makeSwap(9),
constantGas: GasFastestStep,
minStack: minSwapStack(10),
maxStack: maxSwapStack(10),
- valid: true,
},
SWAP10: {
execute: makeSwap(10),
constantGas: GasFastestStep,
minStack: minSwapStack(11),
maxStack: maxSwapStack(11),
- valid: true,
},
SWAP11: {
execute: makeSwap(11),
constantGas: GasFastestStep,
minStack: minSwapStack(12),
maxStack: maxSwapStack(12),
- valid: true,
},
SWAP12: {
execute: makeSwap(12),
constantGas: GasFastestStep,
minStack: minSwapStack(13),
maxStack: maxSwapStack(13),
- valid: true,
},
SWAP13: {
execute: makeSwap(13),
constantGas: GasFastestStep,
minStack: minSwapStack(14),
maxStack: maxSwapStack(14),
- valid: true,
},
SWAP14: {
execute: makeSwap(14),
constantGas: GasFastestStep,
minStack: minSwapStack(15),
maxStack: maxSwapStack(15),
- valid: true,
},
SWAP15: {
execute: makeSwap(15),
constantGas: GasFastestStep,
minStack: minSwapStack(16),
maxStack: maxSwapStack(16),
- valid: true,
},
SWAP16: {
execute: makeSwap(16),
constantGas: GasFastestStep,
minStack: minSwapStack(17),
maxStack: maxSwapStack(17),
- valid: true,
},
LOG0: {
execute: makeLog(0),
@@ -1063,8 +954,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryLog,
- valid: true,
- writes: true,
},
LOG1: {
execute: makeLog(1),
@@ -1072,8 +961,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryLog,
- valid: true,
- writes: true,
},
LOG2: {
execute: makeLog(2),
@@ -1081,8 +968,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(4, 0),
maxStack: maxStack(4, 0),
memorySize: memoryLog,
- valid: true,
- writes: true,
},
LOG3: {
execute: makeLog(3),
@@ -1090,8 +975,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(5, 0),
maxStack: maxStack(5, 0),
memorySize: memoryLog,
- valid: true,
- writes: true,
},
LOG4: {
execute: makeLog(4),
@@ -1099,8 +982,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(6, 0),
maxStack: maxStack(6, 0),
memorySize: memoryLog,
- valid: true,
- writes: true,
},
CREATE: {
execute: opCreate,
@@ -1109,9 +990,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(3, 1),
maxStack: maxStack(3, 1),
memorySize: memoryCreate,
- valid: true,
- writes: true,
- returns: true,
},
CALL: {
execute: opCall,
@@ -1120,8 +998,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
memorySize: memoryCall,
- valid: true,
- returns: true,
},
CALLCODE: {
execute: opCallCode,
@@ -1130,8 +1006,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
memorySize: memoryCall,
- valid: true,
- returns: true,
},
RETURN: {
execute: opReturn,
@@ -1139,17 +1013,21 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryReturn,
- halts: true,
- valid: true,
},
SELFDESTRUCT: {
- execute: opSuicide,
+ execute: opSelfdestruct,
dynamicGas: gasSelfdestruct,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
- halts: true,
- valid: true,
- writes: true,
},
}
+
+ // Fill all unassigned slots with opUndefined.
+ for i, entry := range tbl {
+ if entry == nil {
+ tbl[i] = &operation{execute: opUndefined, maxStack: maxStack(0, 0)}
+ }
+ }
+
+ return tbl
}
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 970ff90fb36e..6cdb7a7c5bed 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -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
diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go
index 2eeba8dfacc3..4a63120a3df4 100644
--- a/core/vm/logger_json.go
+++ b/core/vm/logger_json.go
@@ -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})
}
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index e3e0a085b998..f6c15d9cbb0f 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -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 {
diff --git a/core/vm/memory.go b/core/vm/memory.go
index 7fc44a096188..35b729996075 100644
--- a/core/vm/memory.go
+++ b/core/vm/memory.go
@@ -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("####################")
-}
diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go
index 4fcb41442c4e..e35ca84e0efa 100644
--- a/core/vm/memory_table.go
+++ b/core/vm/memory_table.go
@@ -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))
}
diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go
index 322e01d17c99..26523e53ce42 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -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,
}
diff --git a/core/vm/stack.go b/core/vm/stack.go
index c9c3d07f4b6f..a389c04b725d 100644
--- a/core/vm/stack.go
+++ b/core/vm/stack.go
@@ -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]
}
diff --git a/eth/tracers/testdata/call_tracer_inner_throw_outer_revert.json b/eth/tracers/testdata/call_tracer_inner_throw_outer_revert.json
index 7627c8c23d68..ec2ceb426fda 100644
--- a/eth/tracers/testdata/call_tracer_inner_throw_outer_revert.json
+++ b/eth/tracers/testdata/call_tracer_inner_throw_outer_revert.json
@@ -59,7 +59,7 @@
"result": {
"calls": [
{
- "error": "invalid opcode: opcode 0xfe not defined",
+ "error": "invalid opcode: INVALID",
"from": "0x33056b5dcac09a9b4becad0e1dcf92c19bd0af76",
"gas": "0x75fe3",
"gasUsed": "0x75fe3",
diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go
index 94c828fc900e..235f752bac0a 100644
--- a/eth/tracers/tracer.go
+++ b/eth/tracers/tracer.go
@@ -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
diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go
index a7266c178880..577fd1e576c3 100644
--- a/eth/tracers/tracer_test.go
+++ b/eth/tracers/tracer_test.go
@@ -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) {}
diff --git a/go.mod b/go.mod
index e2065745fde8..8d19bc4956fc 100644
--- a/go.mod
+++ b/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
diff --git a/go.sum b/go.sum
index 70a3ac6c9348..19df00ede131 100644
--- a/go.sum
+++ b/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=
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 242584e863d6..519d4490ab24 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -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.
diff --git a/internal/jsre/deps/web3.js b/internal/jsre/deps/web3.js
index 95b0d1aaf734..5c7236ed1442 100644
--- a/internal/jsre/deps/web3.js
+++ b/internal/jsre/deps/web3.js
@@ -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,
diff --git a/miner/worker.go b/miner/worker.go
index bc3982094e87..6967ae5207eb 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -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.
diff --git a/mobile/android_test.go b/mobile/android_test.go
index 9bb5fd0c4f79..8cc4daacb8a8 100644
--- a/mobile/android_test.go
+++ b/mobile/android_test.go
@@ -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")
diff --git a/params/config.go b/params/config.go
index 86864d9f8b88..933d5f2980a9 100644
--- a/params/config.go
+++ b/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),
}
}
diff --git a/params/protocol_params.go b/params/protocol_params.go
index f7ab3cdd1046..f15530649750 100644
--- a/params/protocol_params.go
+++ b/params/protocol_params.go
@@ -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.
diff --git a/params/version.go b/params/version.go
index ea91eea3d59e..f458365c4801 100644
--- a/params/version.go
+++ b/params/version.go
@@ -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
)
diff --git a/rpc/http.go b/rpc/http.go
index e43597a992ac..e7b90acbe339 100644
--- a/rpc/http.go
+++ b/rpc/http.go
@@ -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,
}
}
diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go
index 509022bf5149..bd516a182721 100644
--- a/tests/vm_test_util.go
+++ b/tests/vm_test_util.go
@@ -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)
}