Merge pull request #470 from XinFinOrg/dev-upgrade

Dev upgrade monthly to master
This commit is contained in:
Liam 2024-03-11 20:11:55 +11:00 committed by GitHub
commit a55f9b5d4c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
77 changed files with 2798 additions and 1426 deletions

176
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,176 @@
name: CI
on:
push:
branches:
- '*'
tags:
- '*'
env:
GOPROXY: https://proxy.golang.org
GO111MODULE: on
tf_version: "1.3.0"
tf_init_cli_options: "-input=false"
tf_validation_cli_options: ""
tf_plan_cli_options: "-lock=false -input=false"
tf_apply_cli_options: "-auto-approve -input=false"
jobs:
tests:
env:
GOPATH: ${{ github.workspace }}
GOBIN: ${{ github.workspace }}/bin
runs-on: ubuntu-latest
defaults:
run:
working-directory: ${{ env.GOPATH }}/src/XDPoSChain
strategy:
matrix:
include:
- name: A-B tests
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[a-b].*")
- name: C-[a-m] tests
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/c[a-m].*")
- name: C-[n-o] tests
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/c[n-o].*")
- name: C-[p-z] tests
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/c[p-z].*")
- name: D-I tests
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[d-i].*")
- name: J-N tests
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[j-n].*")
- name: O-R tests
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[o-r].*")
- name: S tests
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/s.*")
- name: T-Z tests
script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[t-z].*")
steps:
- name: Check out code
uses: actions/checkout@v4
with:
path: ${{ env.GOPATH }}/src/XDPoSChain
- name: Set up Go
uses: actions/setup-go@v5
with:
cache: false
go-version: '1.21.x'
- name: Run tests
run: ${{ matrix.script }}
env:
GO111MODULE: auto
tag_build:
runs-on: ubuntu-latest
needs: tests
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/checkout@v4
- name: Login to Docker Hub
run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
- name: Build Docker image
run: docker build -t xinfinorg/xdposchain:${GITHUB_REF#refs/tags/} -f cicd/Dockerfile .
- name: Push Docker image
run: docker push xinfinorg/xdposchain:${GITHUB_REF#refs/tags/}
devnet_build_push:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/')
needs: tests
steps:
- uses: actions/checkout@v4
- name: Login to Docker Hub
run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
- name: Build and Push Docker images
run: |
docker pull xinfinorg/devnet:latest
docker tag xinfinorg/devnet:latest xinfinorg/devnet:previous
docker rmi xinfinorg/devnet:latest
docker build -t xinfinorg/devnet:latest -f cicd/Dockerfile .
docker push xinfinorg/devnet:latest
docker push xinfinorg/devnet:previous
devnet_terraform_apply:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/')
needs: devnet_build_push
environment: devnet
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
steps:
- uses: actions/checkout@v4
- name: Terraform Apply
run: |
cd cicd/devnet/terraform
terraform init ${{ env.tf_init_cli_options }}
terraform apply ${{ env.tf_apply_cli_options }}
sleep 5
source .env
for ((i=$us_east_2_start;i<$us_east_2_end;i++)); do
echo "Force deploy xdc-$i"
aws ecs update-service --region us-east-2 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager | head -n 10;
done
for ((i=$eu_west_1_start;i<$eu_west_1_end;i++)); do
echo "Force deploy xdc-$i"
aws ecs update-service --region eu-west-1 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager | head -n 10;
done
for ((i=$ap_southeast_2_start;i<$ap_southeast_2_end;i++)); do
echo "Force deploy xdc-$i"
aws ecs update-service --region ap-southeast-2 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager | head -n 10;
done
aws ecs update-service --region ap-southeast-1 --cluster devnet-xdcnode-cluster --service ecs-service-rpc1 --force-new-deployment --no-cli-pager | head -n 10;
rpcnode_terraform_apply:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/')
needs: devnet_build_push
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
steps:
- uses: actions/checkout@v4
- name: Terraform Apply
run: |
cd cicd/terraform
terraform init ${{ env.tf_init_cli_options }}
terraform apply ${{ env.tf_apply_cli_options }}
testnet_dev-upgrade_node:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/')
needs: rpcnode_terraform_apply
environment: testnet
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
steps:
- uses: actions/checkout@v4
- name: ECS Update
run: |
aws ecs update-service --region ap-southeast-1 --cluster testnet-xdcnode-cluster --service ecs-service-testnet-rpc1 --force-new-deployment --no-cli-pager | head -n 10;
mainnet_dev-upgrade_node:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/')
needs: rpcnode_terraform_apply
environment: mainnet
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
steps:
- uses: actions/checkout@v4
- name: ECS Update
run: |
aws ecs update-service --region ap-southeast-1 --cluster mainnet-xdcnode-cluster --service ecs-service-mainnet-rpc1 --force-new-deployment --no-cli-pager | head -n 10;
devnet_send_notification:
runs-on: ubuntu-latest
needs: devnet_terraform_apply
if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/checkout@v4
- name: Send deployment notification
run: |
curl --location --request POST "66.94.98.186:8080/deploy?environment=devnet&service=xdc&version=${GITHUB_SHA}"

3
.gitignore vendored
View file

@ -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

View file

@ -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).

View file

@ -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
``

View file

@ -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

View file

@ -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}"}

View file

@ -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}")}"

View file

@ -7,10 +7,10 @@ then
exit 1
fi
echo $PRIVATE_KEY >> /tmp/key
wallet=$(XDC account import --password .pwd --datadir /work/xdcchain /tmp/key | awk -v FS="({|})" '{print $2}')
wallet=$(XDC account import --password .pwd --datadir /work/xdcchain /tmp/key | awk -F '[{}]' '{print $2}')
XDC --datadir /work/xdcchain init /work/genesis.json
else
wallet=$(XDC account list --datadir /work/xdcchain | head -n 1 | awk -v FS="({|})" '{print $2}')
wallet=$(XDC account list --datadir /work/xdcchain | head -n 1 | awk -F '[{}]' '{print $2}')
fi
input="/work/bootnodes.list"
@ -76,9 +76,9 @@ XDC --ethstats ${netstats} --gcmode archive \
--datadir /work/xdcchain --networkid 50 \
-port $port --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 \
--rpcport $rpc_port \
--rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,XDPoS \
--rpcapi admin,db,eth,debug,net,shh,txpool,personal,web3,XDPoS \
--rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \
--gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \
--periodicprofile --debugdatadir /work/xdcchain \
--ws --wsaddr=0.0.0.0 --wsport $ws_port \
--debugdatadir /work/xdcchain \
--enable-0x-prefix --ws --wsaddr=0.0.0.0 --wsport $ws_port \
--wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log

13
cicd/terraform/.env Normal file
View file

@ -0,0 +1,13 @@
log_level=3
# Ohio
us_east_2_start=0
us_east_2_end=36
# Ireland
eu_west_1_start=37
eu_west_1_end=72
# Sydney
ap_southeast_2_start=73
ap_southeast_2_end=108

28
cicd/terraform/iam.tf Normal file
View file

@ -0,0 +1,28 @@
# IAM policies
data "aws_iam_policy_document" "xdc_ecs_tasks_execution_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ecs-tasks.amazonaws.com"]
}
}
}
# Create the role
resource "aws_iam_role" "xdc_ecs_tasks_execution_role" {
name = "xdc-ecs-task-execution-role"
assume_role_policy = "${data.aws_iam_policy_document.xdc_ecs_tasks_execution_role.json}"
}
# Attached the AWS managed policies to the new role
resource "aws_iam_role_policy_attachment" "xdc_ecs_tasks_execution_role" {
for_each = toset([
"arn:aws:iam::aws:policy/AmazonElasticFileSystemClientFullAccess",
"arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy",
"arn:aws:iam::aws:policy/AmazonElasticFileSystemsUtils"
])
role = aws_iam_role.xdc_ecs_tasks_execution_role.name
policy_arn = each.value
}

59
cicd/terraform/main.tf Normal file
View file

@ -0,0 +1,59 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.13.1"
}
}
}
# Default
provider "aws" {
region = "us-east-1"
}
# WARNING: APSE-1 will only be used to host rpc node
# Workaround to avoid conflicts with existing ecs cluster in existing regions
provider "aws" {
alias = "ap-southeast-1"
region = "ap-southeast-1"
}
module "testnet-rpc" {
source = "./module/region"
region = "ap-southeast-1"
nodeKeys = local.rpcTestnetNodeKeys
enableFixedIp = true
logLevel = local.logLevel
xdc_ecs_tasks_execution_role_arn = aws_iam_role.xdc_ecs_tasks_execution_role.arn
cpu = 1024
memory = 4096
network = "testnet"
vpc_cidr = "10.1.0.0/16"
subnet_cidr = "10.1.0.0/20"
providers = {
aws = aws.ap-southeast-1
}
}
module "mainnet-rpc" {
source = "./module/region"
region = "ap-southeast-1"
nodeKeys = local.rpcMainnetNodeKeys
enableFixedIp = true
logLevel = local.logLevel
xdc_ecs_tasks_execution_role_arn = aws_iam_role.xdc_ecs_tasks_execution_role.arn
cpu = 1024
memory = 4096
network = "mainnet"
vpc_cidr = "10.2.0.0/16"
subnet_cidr = "10.2.0.0/20"
providers = {
aws = aws.ap-southeast-1
}
}

View file

@ -0,0 +1,44 @@
[
{
"name": "tfXdcNode",
"image": "xinfinorg/${image_environment}:${image_tag}",
"environment": [
{"name": "PRIVATE_KEY", "value": "${private_key}"},
{"name": "LOG_LEVEL", "value": "${log_level}"},
{"name": "NODE_NAME", "value": "${node_name}"},
{"name": "NETWORK", "value": "${chain_network}"}
],
"essential": true,
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "${cloudwatch_group}",
"awslogs-region": "${cloudwatch_region}",
"awslogs-stream-prefix": "ecs"
}
},
"portMappings": [
{
"hostPort": 8555,
"protocol": "tcp",
"containerPort": 8555
},
{
"hostPort": 8545,
"protocol": "tcp",
"containerPort": 8545
},
{
"hostPort": 30303,
"protocol": "tcp",
"containerPort": 30303
}
],
"mountPoints": [
{
"containerPath": "/work/xdcchain",
"sourceVolume": "efs"
}
]
}
]

View file

@ -0,0 +1,96 @@
data template_file container_definition {
for_each = var.nodeKeys
template = "${file("${path.module}/container-definition.tpl")}"
vars = {
image_environment = "${lookup(each.value, "imageEnvironment", "devnet")}"
image_tag = "${lookup(each.value, "imageTag", "latest")}"
node_name = "${each.key}"
private_key = "${each.value.pk}"
cloudwatch_group = "tf-${each.key}"
cloudwatch_region = "${var.region}"
log_level = "${lookup(each.value, "logLevel", "${var.logLevel}")}"
chain_network = var.network
}
}
resource "aws_ecs_task_definition" "task_definition_group" {
for_each = var.nodeKeys
family = "${var.network}-${each.key}"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
container_definitions = data.template_file.container_definition[each.key].rendered
execution_role_arn = var.xdc_ecs_tasks_execution_role_arn
task_role_arn = var.xdc_ecs_tasks_execution_role_arn
# New nodes will consume a lot more CPU usage than existing nodes.
# This is due to sync is resource heavy. Recommending set to below if doing sync:
# CPU = 2048, Memory = 4096
# Please set it back to cpu 256 and memory of 2048 after sync is done to save the cost
# cpu = 256
# memory = 2048
cpu = var.cpu
memory = var.memory
volume {
name = "efs"
efs_volume_configuration {
file_system_id = aws_efs_file_system.efs[each.key].id
root_directory = "/"
transit_encryption = "ENABLED"
authorization_config {
access_point_id = aws_efs_access_point.efs_access_point[each.key].id
iam = "DISABLED"
}
}
}
tags = {
Name = "Tf${var.network}Ecs-${each.key}"
}
}
data "aws_ecs_task_definition" "ecs_task_definition" {
for_each = var.nodeKeys
task_definition = aws_ecs_task_definition.task_definition_group[each.key].family
}
# ECS cluster
resource "aws_ecs_cluster" "ecs_cluster" {
name = "${var.network}-xdcnode-cluster"
tags = {
Name = "Tf${var.network}EcsCluster"
}
}
resource "aws_ecs_service" "ecs_service" {
for_each = var.enableFixedIp ? {} : var.nodeKeys
name = "ecs-service-${each.key}"
cluster = aws_ecs_cluster.ecs_cluster.id
task_definition = "${aws_ecs_task_definition.task_definition_group[each.key].family}:${max(aws_ecs_task_definition.task_definition_group[each.key].revision, data.aws_ecs_task_definition.ecs_task_definition[each.key].revision)}"
launch_type = "FARGATE"
scheduling_strategy = "REPLICA"
desired_count = 1
force_new_deployment = true
deployment_minimum_healthy_percent = 0
deployment_maximum_percent = 100
network_configuration {
subnets = [aws_subnet.subnet.id]
assign_public_ip = true
security_groups = [
aws_default_security_group.xdcnode_security_group.id
]
}
deployment_circuit_breaker {
enable = true
rollback = false
}
tags = {
Name = "Tf${var.network}EcsService-${each.key}"
}
}

View file

@ -0,0 +1,67 @@
# EFS
resource "aws_security_group" "efs_security_group" {
name = "Tf${var.network}EfsSecurityGroup"
description = "Allow HTTP in and out of ${var.network} EFS"
vpc_id = aws_vpc.vpc.id
ingress {
from_port = 2049
to_port = 2049
protocol = "TCP"
security_groups = [aws_default_security_group.xdcnode_security_group.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "Tf${var.network}Efs"
}
}
resource "aws_efs_file_system" "efs" {
for_each = var.nodeKeys
creation_token = "efs-${each.key}"
performance_mode = "generalPurpose"
throughput_mode = "bursting"
encrypted = "true"
lifecycle_policy {
transition_to_ia = "AFTER_30_DAYS"
}
tags = {
Name = "Tf${var.network}Efs${each.key}"
}
}
resource "aws_efs_mount_target" "efs_efs_mount_target" {
for_each = var.nodeKeys
file_system_id = aws_efs_file_system.efs[each.key].id
subnet_id = aws_subnet.subnet.id
security_groups = [aws_security_group.efs_security_group.id]
}
resource "aws_efs_access_point" "efs_access_point" {
for_each = var.nodeKeys
file_system_id = aws_efs_file_system.efs[each.key].id
root_directory {
path = "/${each.key}/database"
creation_info {
owner_gid = 1001
owner_uid = 1001
permissions = 777
}
}
posix_user {
gid = 1001
uid = 1001
secondary_gids = [0]
}
tags = {
Name = "Tf${var.network}EfsAccessPoint${each.key}"
}
}

View file

@ -0,0 +1,103 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.13.1"
}
}
}
resource "aws_vpc" "vpc" {
cidr_block = var.vpc_cidr
instance_tenancy = "default"
enable_dns_hostnames = true
tags = {
Name = "Tf${var.network}Vpc"
}
}
resource "aws_subnet" "subnet" {
vpc_id = aws_vpc.vpc.id
cidr_block = var.subnet_cidr
map_public_ip_on_launch = true
tags = {
Name = "Tf${var.network}VpcSubnet"
}
}
resource "aws_internet_gateway" "gatewat" {
vpc_id = aws_vpc.vpc.id
tags = {
Name = "Tf${var.network}Gateway"
}
}
resource "aws_route_table" "route_table" {
vpc_id = aws_vpc.vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.gatewat.id
}
tags = {
Name = "Tf${var.network}VpcRoutingTable"
}
}
resource "aws_route_table_association" "route_table_association" {
subnet_id = aws_subnet.subnet.id
route_table_id = aws_route_table.route_table.id
}
resource "aws_default_security_group" "xdcnode_security_group" {
vpc_id = aws_vpc.vpc.id
ingress {
description = "listener port"
from_port = 30303
to_port = 30303
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "discovery port"
from_port = 30303
to_port = 30303
protocol = "udp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "rpc port"
from_port = 8545
to_port = 8545
protocol = "tcp"
cidr_blocks = [var.vpc_cidr]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "Tf${var.network}Node"
}
}
# Logs
resource "aws_cloudwatch_log_group" "cloud_watch_group" {
for_each = var.nodeKeys
name = "tf-${each.key}"
retention_in_days = 14 # Logs are only kept for 14 days
tags = {
Name = "Tf${var.network}CloudWatchGroup${each.key}"
}
}

View file

@ -0,0 +1,104 @@
# Allocate an Elastic IP for the NLB
resource "aws_eip" "nlb_eip" {
domain = "vpc"
}
# Create a Network Load Balancer
resource "aws_lb" "rpc_node_nlb" {
count = var.enableFixedIp ? 1 : 0
name = "${var.network}-rpc-node-nlb"
load_balancer_type = "network"
enable_deletion_protection = false
subnet_mapping {
subnet_id = aws_subnet.subnet.id
allocation_id = aws_eip.nlb_eip.id
}
}
# Listener and Target Group for the rpc node container
resource "aws_lb_target_group" "rpc_node_tg_8545" {
count = var.enableFixedIp ? 1 : 0
name = "${var.network}-rpc-node-tg"
port = 8545
protocol = "TCP"
vpc_id = aws_vpc.vpc.id
target_type = "ip"
}
resource "aws_lb_listener" "rpc_node_listener_8545" {
count = var.enableFixedIp ? 1 : 0
load_balancer_arn = aws_lb.rpc_node_nlb[0].arn
port = 8545
protocol = "TCP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.rpc_node_tg_8545[0].arn
}
}
resource "aws_ecs_service" "rpc_node_ecs_service" {
for_each = var.enableFixedIp ? var.nodeKeys : {}
name = "ecs-service-${each.key}"
cluster = aws_ecs_cluster.ecs_cluster.id
task_definition = "${aws_ecs_task_definition.task_definition_group[each.key].family}:${max(aws_ecs_task_definition.task_definition_group[each.key].revision, data.aws_ecs_task_definition.ecs_task_definition[each.key].revision)}"
launch_type = "FARGATE"
scheduling_strategy = "REPLICA"
desired_count = 1
force_new_deployment = true
deployment_minimum_healthy_percent = 0
deployment_maximum_percent = 100
network_configuration {
subnets = [aws_subnet.subnet.id]
assign_public_ip = true
security_groups = [
aws_default_security_group.xdcnode_security_group.id
]
}
deployment_circuit_breaker {
enable = true
rollback = false
}
load_balancer {
target_group_arn = aws_lb_target_group.rpc_node_tg_8545[0].arn
container_name = "tfXdcNode"
container_port = 8545
}
depends_on = [
aws_lb_listener.rpc_node_listener_8545
]
tags = {
Name = "TfRpcNodeEcsService-${each.key}"
}
}
# Target Group for port 30303
resource "aws_lb_target_group" "rpc_node_tg_30303" {
count = var.enableFixedIp ? 1 : 0
name = "${var.network}-rpc-node-tg-30303"
port = 30303
protocol = "TCP"
vpc_id = aws_vpc.vpc.id
target_type = "ip"
}
# Listener for port 30303
resource "aws_lb_listener" "rpc_node_listener_30303" {
count = var.enableFixedIp ? 1 : 0
load_balancer_arn = aws_lb.rpc_node_nlb[0].arn
port = 30303
protocol = "TCP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.rpc_node_tg_30303[0].arn
}
}

View file

@ -0,0 +1,50 @@
variable "region" {
description = "AWS region"
type = string
}
variable "nodeKeys" {
description = "each miner's key"
type = map
}
variable "logLevel" {
description = "containers log level"
type = string
}
variable "xdc_ecs_tasks_execution_role_arn" {
description = "aws iam role resource arn"
type = string
}
variable "enableFixedIp" {
description = "a flag to indicate whether fixed ip should be associated to the nodes. This is used for RPC node"
type = bool
default = false
}
variable "network" {
description = "blockchain network"
type = string
}
variable "cpu" {
description = "container cpu"
type = number
}
variable "memory" {
description = "container memory"
type = number
}
variable "vpc_cidr" {
description = "vpc cidr"
type = string
}
variable "subnet_cidr" {
description = "subnet cidr"
type = string
}

14
cicd/terraform/s3.tf Normal file
View file

@ -0,0 +1,14 @@
# Bucket need to be created first. If first time run terraform init, need to comment out the below section
terraform {
backend "s3" {
bucket = "tf-xinfin-bucket"
key = "tf/terraform.tfstate"
region = "us-east-1"
encrypt = true
}
}
data "aws_s3_object" "xdc_node_config" {
bucket = "tf-xinfin-bucket"
key = "node-config.json"
}

View file

@ -0,0 +1,35 @@
locals {
/**
Load the nodes data from s3
Below is the the format the config needs to follow:
{{Name of the node, in a pattern of 'xdc'+ number. i.e xdc50}}: {
pk: {{Value of the node private key}},
... any other configuration we want to pass.
}
Note: No `n` is allowed in the node name
**/
predefinedNodesConfig = jsondecode(data.aws_s3_object.xdc_node_config.body)
envs = { for tuple in regexall("(.*)=(.*)", file(".env")) : tuple[0] => tuple[1] }
logLevel = local.envs["log_level"]
# regions = [
# {
# "name": "us-east-2", // Ohio
# "start": local.envs["us_east_2_start"],
# "end": local.envs["us_east_2_end"],
# }
# ]
# keyNames = {
# for r in local.regions :
# r.name => [for i in range(r.start, r.end+1) : "xdc${i}"]
# }
# nodeKeys = {
# for r in local.regions :
# r.name => { for i in local.keyNames[r.name]: i => local.predefinedNodesConfig[i] }
# }
rpcTestnetNodeKeys = { "testnet-rpc1": local.predefinedNodesConfig["testnet-rpc1"]} // we hardcode the rpc to a single node for now
rpcMainnetNodeKeys = { "mainnet-rpc1": local.predefinedNodesConfig["mainnet-rpc1"]} // we hardcode the rpc to a single node for now
}

View file

@ -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

View file

@ -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
}

View file

@ -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)
}

View file

@ -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

View file

@ -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

View file

@ -45,6 +45,11 @@ var TIPXDCX = big.NewInt(23779191)
var TIPXDCXLending = big.NewInt(23779191)
var TIPXDCXCancellationFee = big.NewInt(23779191)
var TIPXDCXCancellationFeeTestnet = big.NewInt(23779191)
var TIPXDCXDISABLE = big.NewInt(99999999900)
var BerlinBlock = big.NewInt(9999999999)
var LondonBlock = big.NewInt(9999999999)
var MergeBlock = big.NewInt(9999999999)
var ShanghaiBlock = big.NewInt(9999999999)
var TIPXDCXTestnet = big.NewInt(23779191)
var IsTestnet bool = false

223
common/lru/basiclru.go Normal file
View file

@ -0,0 +1,223 @@
// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package lru implements generically-typed LRU caches.
package lru
// BasicLRU is a simple LRU cache.
//
// This type is not safe for concurrent use.
// The zero value is not valid, instances must be created using NewCache.
type BasicLRU[K comparable, V any] struct {
list *list[K]
items map[K]cacheItem[K, V]
cap int
}
type cacheItem[K any, V any] struct {
elem *listElem[K]
value V
}
// NewBasicLRU creates a new LRU cache.
func NewBasicLRU[K comparable, V any](capacity int) BasicLRU[K, V] {
if capacity <= 0 {
capacity = 1
}
c := BasicLRU[K, V]{
items: make(map[K]cacheItem[K, V]),
list: newList[K](),
cap: capacity,
}
return c
}
// Add adds a value to the cache. Returns true if an item was evicted to store the new item.
func (c *BasicLRU[K, V]) Add(key K, value V) (evicted bool) {
item, ok := c.items[key]
if ok {
// Already exists in cache.
item.value = value
c.items[key] = item
c.list.moveToFront(item.elem)
return false
}
var elem *listElem[K]
if c.Len() >= c.cap {
elem = c.list.removeLast()
delete(c.items, elem.v)
evicted = true
} else {
elem = new(listElem[K])
}
// Store the new item.
// Note that, if another item was evicted, we re-use its list element here.
elem.v = key
c.items[key] = cacheItem[K, V]{elem, value}
c.list.pushElem(elem)
return evicted
}
// Contains reports whether the given key exists in the cache.
func (c *BasicLRU[K, V]) Contains(key K) bool {
_, ok := c.items[key]
return ok
}
// Get retrieves a value from the cache. This marks the key as recently used.
func (c *BasicLRU[K, V]) Get(key K) (value V, ok bool) {
item, ok := c.items[key]
if !ok {
return value, false
}
c.list.moveToFront(item.elem)
return item.value, true
}
// GetOldest retrieves the least-recently-used item.
// Note that this does not update the item's recency.
func (c *BasicLRU[K, V]) GetOldest() (key K, value V, ok bool) {
lastElem := c.list.last()
if lastElem == nil {
return key, value, false
}
key = lastElem.v
item := c.items[key]
return key, item.value, true
}
// Len returns the current number of items in the cache.
func (c *BasicLRU[K, V]) Len() int {
return len(c.items)
}
// Peek retrieves a value from the cache, but does not mark the key as recently used.
func (c *BasicLRU[K, V]) Peek(key K) (value V, ok bool) {
item, ok := c.items[key]
return item.value, ok
}
// Purge empties the cache.
func (c *BasicLRU[K, V]) Purge() {
c.list.init()
for k := range c.items {
delete(c.items, k)
}
}
// Remove drops an item from the cache. Returns true if the key was present in cache.
func (c *BasicLRU[K, V]) Remove(key K) bool {
item, ok := c.items[key]
if ok {
delete(c.items, key)
c.list.remove(item.elem)
}
return ok
}
// RemoveOldest drops the least recently used item.
func (c *BasicLRU[K, V]) RemoveOldest() (key K, value V, ok bool) {
lastElem := c.list.last()
if lastElem == nil {
return key, value, false
}
key = lastElem.v
item := c.items[key]
delete(c.items, key)
c.list.remove(lastElem)
return key, item.value, true
}
// Keys returns all keys in the cache.
func (c *BasicLRU[K, V]) Keys() []K {
keys := make([]K, 0, len(c.items))
return c.list.appendTo(keys)
}
// list is a doubly-linked list holding items of type he.
// The zero value is not valid, use newList to create lists.
type list[T any] struct {
root listElem[T]
}
type listElem[T any] struct {
next *listElem[T]
prev *listElem[T]
v T
}
func newList[T any]() *list[T] {
l := new(list[T])
l.init()
return l
}
// init reinitializes the list, making it empty.
func (l *list[T]) init() {
l.root.next = &l.root
l.root.prev = &l.root
}
// push adds an element to the front of the list.
func (l *list[T]) pushElem(e *listElem[T]) {
e.prev = &l.root
e.next = l.root.next
l.root.next = e
e.next.prev = e
}
// moveToFront makes 'node' the head of the list.
func (l *list[T]) moveToFront(e *listElem[T]) {
e.prev.next = e.next
e.next.prev = e.prev
l.pushElem(e)
}
// remove removes an element from the list.
func (l *list[T]) remove(e *listElem[T]) {
e.prev.next = e.next
e.next.prev = e.prev
e.next, e.prev = nil, nil
}
// removeLast removes the last element of the list.
func (l *list[T]) removeLast() *listElem[T] {
last := l.last()
if last != nil {
l.remove(last)
}
return last
}
// last returns the last element of the list, or nil if the list is empty.
func (l *list[T]) last() *listElem[T] {
e := l.root.prev
if e == &l.root {
return nil
}
return e
}
// appendTo appends all list elements to a slice.
func (l *list[T]) appendTo(slice []T) []T {
for e := l.root.prev; e != &l.root; e = e.prev {
slice = append(slice, e.v)
}
return slice
}

255
common/lru/basiclru_test.go Normal file
View file

@ -0,0 +1,255 @@
// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package lru
import (
crand "crypto/rand"
"fmt"
"io"
"math/rand"
"testing"
)
// Some of these test cases were adapted
// from https://github.com/hashicorp/golang-lru/blob/master/simplelru/lru_test.go
func TestBasicLRU(t *testing.T) {
cache := NewBasicLRU[int, int](128)
for i := 0; i < 256; i++ {
cache.Add(i, i)
}
if cache.Len() != 128 {
t.Fatalf("bad len: %v", cache.Len())
}
// Check that Keys returns least-recent key first.
keys := cache.Keys()
if len(keys) != 128 {
t.Fatal("wrong Keys() length", len(keys))
}
for i, k := range keys {
v, ok := cache.Peek(k)
if !ok {
t.Fatalf("expected key %d be present", i)
}
if v != k {
t.Fatalf("expected %d == %d", k, v)
}
if v != i+128 {
t.Fatalf("wrong value at key %d: %d, want %d", i, v, i+128)
}
}
for i := 0; i < 128; i++ {
_, ok := cache.Get(i)
if ok {
t.Fatalf("%d should be evicted", i)
}
}
for i := 128; i < 256; i++ {
_, ok := cache.Get(i)
if !ok {
t.Fatalf("%d should not be evicted", i)
}
}
for i := 128; i < 192; i++ {
ok := cache.Remove(i)
if !ok {
t.Fatalf("%d should be in cache", i)
}
ok = cache.Remove(i)
if ok {
t.Fatalf("%d should not be in cache", i)
}
_, ok = cache.Get(i)
if ok {
t.Fatalf("%d should be deleted", i)
}
}
// Request item 192.
cache.Get(192)
// It should be the last item returned by Keys().
for i, k := range cache.Keys() {
if (i < 63 && k != i+193) || (i == 63 && k != 192) {
t.Fatalf("out of order key: %v", k)
}
}
cache.Purge()
if cache.Len() != 0 {
t.Fatalf("bad len: %v", cache.Len())
}
if _, ok := cache.Get(200); ok {
t.Fatalf("should contain nothing")
}
}
func TestBasicLRUAddExistingKey(t *testing.T) {
cache := NewBasicLRU[int, int](1)
cache.Add(1, 1)
cache.Add(1, 2)
v, _ := cache.Get(1)
if v != 2 {
t.Fatal("wrong value:", v)
}
}
// This test checks GetOldest and RemoveOldest.
func TestBasicLRUGetOldest(t *testing.T) {
cache := NewBasicLRU[int, int](128)
for i := 0; i < 256; i++ {
cache.Add(i, i)
}
k, _, ok := cache.GetOldest()
if !ok {
t.Fatalf("missing")
}
if k != 128 {
t.Fatalf("bad: %v", k)
}
k, _, ok = cache.RemoveOldest()
if !ok {
t.Fatalf("missing")
}
if k != 128 {
t.Fatalf("bad: %v", k)
}
k, _, ok = cache.RemoveOldest()
if !ok {
t.Fatalf("missing oldest item")
}
if k != 129 {
t.Fatalf("wrong oldest item: %v", k)
}
}
// Test that Add returns true/false if an eviction occurred
func TestBasicLRUAddReturnValue(t *testing.T) {
cache := NewBasicLRU[int, int](1)
if cache.Add(1, 1) {
t.Errorf("first add shouldn't have evicted")
}
if !cache.Add(2, 2) {
t.Errorf("second add should have evicted")
}
}
// This test verifies that Contains doesn't change item recency.
func TestBasicLRUContains(t *testing.T) {
cache := NewBasicLRU[int, int](2)
cache.Add(1, 1)
cache.Add(2, 2)
if !cache.Contains(1) {
t.Errorf("1 should be in the cache")
}
cache.Add(3, 3)
if cache.Contains(1) {
t.Errorf("Contains should not have updated recency of 1")
}
}
// Test that Peek doesn't update recent-ness
func TestBasicLRUPeek(t *testing.T) {
cache := NewBasicLRU[int, int](2)
cache.Add(1, 1)
cache.Add(2, 2)
if v, ok := cache.Peek(1); !ok || v != 1 {
t.Errorf("1 should be set to 1")
}
cache.Add(3, 3)
if cache.Contains(1) {
t.Errorf("should not have updated recent-ness of 1")
}
}
func BenchmarkLRU(b *testing.B) {
var (
capacity = 1000
indexes = make([]int, capacity*20)
keys = make([]string, capacity)
values = make([][]byte, capacity)
)
for i := range indexes {
indexes[i] = rand.Intn(capacity)
}
for i := range keys {
b := make([]byte, 32)
crand.Read(b)
keys[i] = string(b)
crand.Read(b)
values[i] = b
}
var sink []byte
b.Run("Add/BasicLRU", func(b *testing.B) {
cache := NewBasicLRU[int, int](capacity)
for i := 0; i < b.N; i++ {
cache.Add(i, i)
}
})
b.Run("Get/BasicLRU", func(b *testing.B) {
cache := NewBasicLRU[string, []byte](capacity)
for i := 0; i < capacity; i++ {
index := indexes[i]
cache.Add(keys[index], values[index])
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
k := keys[indexes[i%len(indexes)]]
v, ok := cache.Get(k)
if ok {
sink = v
}
}
})
// // vs. github.com/hashicorp/golang-lru/simplelru
// b.Run("Add/simplelru.LRU", func(b *testing.B) {
// cache, _ := simplelru.NewLRU(capacity, nil)
// for i := 0; i < b.N; i++ {
// cache.Add(i, i)
// }
// })
// b.Run("Get/simplelru.LRU", func(b *testing.B) {
// cache, _ := simplelru.NewLRU(capacity, nil)
// for i := 0; i < capacity; i++ {
// index := indexes[i]
// cache.Add(keys[index], values[index])
// }
//
// b.ResetTimer()
// for i := 0; i < b.N; i++ {
// k := keys[indexes[i%len(indexes)]]
// v, ok := cache.Get(k)
// if ok {
// sink = v.([]byte)
// }
// }
// })
fmt.Fprintln(io.Discard, sink)
}

84
common/lru/blob_lru.go Normal file
View file

@ -0,0 +1,84 @@
// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package lru
import (
"math"
"sync"
)
// blobType is the type constraint for values stored in SizeConstrainedCache.
type blobType interface {
~[]byte | ~string
}
// SizeConstrainedCache is a cache where capacity is in bytes (instead of item count). When the cache
// is at capacity, and a new item is added, older items are evicted until the size
// constraint is met.
//
// OBS: This cache assumes that items are content-addressed: keys are unique per content.
// In other words: two Add(..) with the same key K, will always have the same value V.
type SizeConstrainedCache[K comparable, V blobType] struct {
size uint64
maxSize uint64
lru BasicLRU[K, V]
lock sync.Mutex
}
// NewSizeConstrainedCache creates a new size-constrained LRU cache.
func NewSizeConstrainedCache[K comparable, V blobType](maxSize uint64) *SizeConstrainedCache[K, V] {
return &SizeConstrainedCache[K, V]{
size: 0,
maxSize: maxSize,
lru: NewBasicLRU[K, V](math.MaxInt),
}
}
// Add adds a value to the cache. Returns true if an eviction occurred.
// OBS: This cache assumes that items are content-addressed: keys are unique per content.
// In other words: two Add(..) with the same key K, will always have the same value V.
// OBS: The value is _not_ copied on Add, so the caller must not modify it afterwards.
func (c *SizeConstrainedCache[K, V]) Add(key K, value V) (evicted bool) {
c.lock.Lock()
defer c.lock.Unlock()
// Unless it is already present, might need to evict something.
// OBS: If it is present, we still call Add internally to bump the recentness.
if !c.lru.Contains(key) {
targetSize := c.size + uint64(len(value))
for targetSize > c.maxSize {
evicted = true
_, v, ok := c.lru.RemoveOldest()
if !ok {
// list is now empty. Break
break
}
targetSize -= uint64(len(v))
}
c.size = targetSize
}
c.lru.Add(key, value)
return evicted
}
// Get looks up a key's value from the cache.
func (c *SizeConstrainedCache[K, V]) Get(key K) (V, bool) {
c.lock.Lock()
defer c.lock.Unlock()
return c.lru.Get(key)
}

155
common/lru/blob_lru_test.go Normal file
View file

@ -0,0 +1,155 @@
// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package lru
import (
"encoding/binary"
"fmt"
"testing"
)
type testKey [8]byte
func mkKey(i int) (key testKey) {
binary.LittleEndian.PutUint64(key[:], uint64(i))
return key
}
func TestSizeConstrainedCache(t *testing.T) {
lru := NewSizeConstrainedCache[testKey, []byte](100)
var want uint64
// Add 11 items of 10 byte each. First item should be swapped out
for i := 0; i < 11; i++ {
k := mkKey(i)
v := fmt.Sprintf("value-%04d", i)
lru.Add(k, []byte(v))
want += uint64(len(v))
if want > 100 {
want = 100
}
if have := lru.size; have != want {
t.Fatalf("size wrong, have %d want %d", have, want)
}
}
// Zero:th should be evicted
{
k := mkKey(0)
if _, ok := lru.Get(k); ok {
t.Fatalf("should be evicted: %v", k)
}
}
// Elems 1-11 should be present
for i := 1; i < 11; i++ {
k := mkKey(i)
want := fmt.Sprintf("value-%04d", i)
have, ok := lru.Get(k)
if !ok {
t.Fatalf("missing key %v", k)
}
if string(have) != want {
t.Fatalf("wrong value, have %v want %v", have, want)
}
}
}
// This test adds inserting an element exceeding the max size.
func TestSizeConstrainedCacheOverflow(t *testing.T) {
lru := NewSizeConstrainedCache[testKey, []byte](100)
// Add 10 items of 10 byte each, filling the cache
for i := 0; i < 10; i++ {
k := mkKey(i)
v := fmt.Sprintf("value-%04d", i)
lru.Add(k, []byte(v))
}
// Add one single large elem. We expect it to swap out all entries.
{
k := mkKey(1337)
v := make([]byte, 200)
lru.Add(k, v)
}
// Elems 0-9 should be missing
for i := 1; i < 10; i++ {
k := mkKey(i)
if _, ok := lru.Get(k); ok {
t.Fatalf("should be evicted: %v", k)
}
}
// The size should be accurate
if have, want := lru.size, uint64(200); have != want {
t.Fatalf("size wrong, have %d want %d", have, want)
}
// Adding one small item should swap out the large one
{
i := 0
k := mkKey(i)
v := fmt.Sprintf("value-%04d", i)
lru.Add(k, []byte(v))
if have, want := lru.size, uint64(10); have != want {
t.Fatalf("size wrong, have %d want %d", have, want)
}
}
}
// This checks what happens when inserting the same k/v multiple times.
func TestSizeConstrainedCacheSameItem(t *testing.T) {
lru := NewSizeConstrainedCache[testKey, []byte](100)
// Add one 10 byte-item 10 times.
k := mkKey(0)
v := fmt.Sprintf("value-%04d", 0)
for i := 0; i < 10; i++ {
lru.Add(k, []byte(v))
}
// The size should be accurate.
if have, want := lru.size, uint64(10); have != want {
t.Fatalf("size wrong, have %d want %d", have, want)
}
}
// This tests that empty/nil values are handled correctly.
func TestSizeConstrainedCacheEmpties(t *testing.T) {
lru := NewSizeConstrainedCache[testKey, []byte](100)
// This test abuses the lru a bit, using different keys for identical value(s).
for i := 0; i < 10; i++ {
lru.Add(testKey{byte(i)}, []byte{})
lru.Add(testKey{byte(255 - i)}, nil)
}
// The size should not count, only the values count. So this could be a DoS
// since it basically has no cap, and it is intentionally overloaded with
// different-keyed 0-length values.
if have, want := lru.size, uint64(0); have != want {
t.Fatalf("size wrong, have %d want %d", have, want)
}
for i := 0; i < 10; i++ {
if v, ok := lru.Get(testKey{byte(i)}); !ok {
t.Fatalf("test %d: expected presence", i)
} else if v == nil {
t.Fatalf("test %d, v is nil", i)
}
if v, ok := lru.Get(testKey{byte(255 - i)}); !ok {
t.Fatalf("test %d: expected presence", i)
} else if v != nil {
t.Fatalf("test %d, v is not nil", i)
}
}
}

95
common/lru/lru.go Normal file
View file

@ -0,0 +1,95 @@
// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package lru
import "sync"
// Cache is a LRU cache.
// This type is safe for concurrent use.
type Cache[K comparable, V any] struct {
cache BasicLRU[K, V]
mu sync.Mutex
}
// NewCache creates an LRU cache.
func NewCache[K comparable, V any](capacity int) *Cache[K, V] {
return &Cache[K, V]{cache: NewBasicLRU[K, V](capacity)}
}
// Add adds a value to the cache. Returns true if an item was evicted to store the new item.
func (c *Cache[K, V]) Add(key K, value V) (evicted bool) {
c.mu.Lock()
defer c.mu.Unlock()
return c.cache.Add(key, value)
}
// Contains reports whether the given key exists in the cache.
func (c *Cache[K, V]) Contains(key K) bool {
c.mu.Lock()
defer c.mu.Unlock()
return c.cache.Contains(key)
}
// Get retrieves a value from the cache. This marks the key as recently used.
func (c *Cache[K, V]) Get(key K) (value V, ok bool) {
c.mu.Lock()
defer c.mu.Unlock()
return c.cache.Get(key)
}
// Len returns the current number of items in the cache.
func (c *Cache[K, V]) Len() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.cache.Len()
}
// Peek retrieves a value from the cache, but does not mark the key as recently used.
func (c *Cache[K, V]) Peek(key K) (value V, ok bool) {
c.mu.Lock()
defer c.mu.Unlock()
return c.cache.Peek(key)
}
// Purge empties the cache.
func (c *Cache[K, V]) Purge() {
c.mu.Lock()
defer c.mu.Unlock()
c.cache.Purge()
}
// Remove drops an item from the cache. Returns true if the key was present in cache.
func (c *Cache[K, V]) Remove(key K) bool {
c.mu.Lock()
defer c.mu.Unlock()
return c.cache.Remove(key)
}
// Keys returns all keys of items currently in the LRU.
func (c *Cache[K, V]) Keys() []K {
c.mu.Lock()
defer c.mu.Unlock()
return c.cache.Keys()
}

View file

@ -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 {

View file

@ -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)

View file

@ -22,53 +22,37 @@ import (
"encoding/hex"
)
// Tests disassembling the instructions for valid evm code
func TestInstructionIteratorValid(t *testing.T) {
cnt := 0
script, _ := hex.DecodeString("61000000")
// Tests disassembling instructions
func TestInstructionIterator(t *testing.T) {
for i, tc := range []struct {
want int
code string
wantErr string
}{
{2, "61000000", ""}, // valid code
{0, "6100", "incomplete push instruction at 0"}, // invalid code
{2, "5900", ""}, // push0
{0, "", ""}, // empty
it := NewInstructionIterator(script)
for it.Next() {
cnt++
}
if err := it.Error(); err != nil {
t.Errorf("Expected 2, but encountered error %v instead.", err)
}
if cnt != 2 {
t.Errorf("Expected 2, but got %v instead.", cnt)
}
}
// Tests disassembling the instructions for invalid evm code
func TestInstructionIteratorInvalid(t *testing.T) {
cnt := 0
script, _ := hex.DecodeString("6100")
it := NewInstructionIterator(script)
for it.Next() {
cnt++
}
if it.Error() == nil {
t.Errorf("Expected an error, but got %v instead.", cnt)
}
}
// Tests disassembling the instructions for empty evm code
func TestInstructionIteratorEmpty(t *testing.T) {
cnt := 0
script, _ := hex.DecodeString("")
it := NewInstructionIterator(script)
for it.Next() {
cnt++
}
if err := it.Error(); err != nil {
t.Errorf("Expected 0, but encountered error %v instead.", err)
}
if cnt != 0 {
t.Errorf("Expected 0, but got %v instead.", cnt)
} {
var (
have int
code, _ = hex.DecodeString(tc.code)
it = NewInstructionIterator(code)
)
for it.Next() {
have++
}
var haveErr = ""
if it.Error() != nil {
haveErr = it.Error().Error()
}
if haveErr != tc.wantErr {
t.Errorf("test %d: encountered error: %q want %q", i, haveErr, tc.wantErr)
continue
}
if have != tc.want {
t.Errorf("wrong instruction count, have %d want %d", have, tc.want)
}
}
}

View file

@ -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,
}
}

View file

@ -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.

View file

@ -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 {

View file

@ -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

View file

@ -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.

View file

@ -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)

View file

@ -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 {

View file

@ -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.

View file

@ -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

View file

@ -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 chains 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
}

View file

@ -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

View file

@ -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}

View file

@ -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

View file

@ -61,7 +61,7 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
// as argument:
// CALLDATACOPY (stack position 2)
// CODECOPY (stack position 2)
// EXTCODECOPY (stack poition 3)
// EXTCODECOPY (stack position 3)
// RETURNDATACOPY (stack position 2)
func memoryCopierGas(stackpos int) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
@ -71,7 +71,7 @@ func memoryCopierGas(stackpos int) gasFunc {
return 0, err
}
// And gas for copying data, charged per word at param.CopyGas
words, overflow := bigUint64(stack.Back(stackpos))
words, overflow := stack.Back(stackpos).Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
@ -97,7 +97,7 @@ var (
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
y, x = stack.Back(1), stack.Back(0)
current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32()))
)
// The legacy gas metering only takes into consideration the current state
// Legacy rules should be applied if we are in Petersburg (removal of EIP-1283)
@ -132,11 +132,11 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
// 2.2.2. If original value equals new value (this storage slot is reset)
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
value := common.BigToHash(y)
value := common.Hash(y.Bytes32())
if current == value { // noop (1)
return params.NetSstoreNoopGas, nil
}
original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return params.NetSstoreInitGas, nil
@ -164,18 +164,18 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
}
// 0. If *gasleft* is less than or equal to 2300, fail the current call.
// 1. If current value equals new value (this is a no-op), SSTORE_NOOP_GAS gas is deducted.
// 1. If current value equals new value (this is a no-op), SLOAD_GAS is deducted.
// 2. If current value does not equal new value:
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context):
// 2.1.1. If original value is 0, SSTORE_INIT_GAS gas is deducted.
// 2.1.2. Otherwise, SSTORE_CLEAN_GAS gas is deducted. If new value is 0, add SSTORE_CLEAR_REFUND to refund counter.
// 2.2. If original value does not equal current value (this storage slot is dirty), SSTORE_DIRTY_GAS gas is deducted. Apply both of the following clauses:
// 2.1.1. If original value is 0, SSTORE_SET_GAS (20K) gas is deducted.
// 2.1.2. Otherwise, SSTORE_RESET_GAS gas is deducted. If new value is 0, add SSTORE_CLEARS_SCHEDULE to refund counter.
// 2.2. If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses:
// 2.2.1. If original value is not 0:
// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEAR_REFUND gas from refund counter. We can prove that refund counter will never go below 0.
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEAR_REFUND gas to refund counter.
// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter.
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter.
// 2.2.2. If original value equals new value (this storage slot is reset):
// 2.2.2.1. If original value is 0, add SSTORE_INIT_REFUND to refund counter.
// 2.2.2.2. Otherwise, add SSTORE_CLEAN_REFUND gas to refund counter.
// 2.2.2.1. If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter.
// 2.2.2.2. Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter.
func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
// If we fail the minimum gas availability invariant, fail (0)
if contract.Gas <= params.SstoreSentryGasEIP2200 {
@ -184,43 +184,43 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
// Gas sentry honoured, do the actual gas calculation based on the stored value
var (
y, x = stack.Back(1), stack.Back(0)
current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32()))
)
value := common.BigToHash(y)
value := common.Hash(y.Bytes32())
if current == value { // noop (1)
return params.SstoreNoopGasEIP2200, nil
return params.SloadGasEIP2200, nil
}
original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return params.SstoreInitGasEIP2200, nil
return params.SstoreSetGasEIP2200, nil
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(params.SstoreClearRefundEIP2200)
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
}
return params.SstoreCleanGasEIP2200, nil // write existing slot (2.1.2)
return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
evm.StateDB.SubRefund(params.SstoreClearRefundEIP2200)
evm.StateDB.SubRefund(params.SstoreClearsScheduleRefundEIP2200)
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
evm.StateDB.AddRefund(params.SstoreClearRefundEIP2200)
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
}
}
if original == value {
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
evm.StateDB.AddRefund(params.SstoreInitRefundEIP2200)
evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
} else { // reset to original existing slot (2.2.2.2)
evm.StateDB.AddRefund(params.SstoreCleanRefundEIP2200)
evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
}
}
return params.SstoreDirtyGasEIP2200, nil // dirty update (2.2)
return params.SloadGasEIP2200, nil // dirty update (2.2)
}
func makeGasLog(n uint64) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
requestedSize, overflow := bigUint64(stack.Back(1))
requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
@ -248,16 +248,16 @@ func makeGasLog(n uint64) gasFunc {
}
}
func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
wordGas, overflow := bigUint64(stack.Back(1))
wordGas, overflow := stack.Back(1).Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow {
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
@ -287,11 +287,11 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
if err != nil {
return 0, err
}
wordGas, overflow := bigUint64(stack.Back(2))
wordGas, overflow := stack.Back(2).Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow {
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
@ -329,8 +329,8 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
gas uint64
transfersValue = stack.Back(2).Sign() != 0
address = common.BigToAddress(stack.Back(1))
transfersValue = !stack.Back(2).IsZero()
address = common.Address(stack.Back(1).Bytes20())
)
if evm.chainRules.IsEIP158 {
if transfersValue && evm.StateDB.Empty(address) {
@ -423,7 +423,7 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
// EIP150 homestead gas reprice fork:
if evm.chainRules.IsEIP150 {
gas = params.SelfdestructGasEIP150
var address = common.BigToAddress(stack.Back(0))
var address = common.Address(stack.Back(0).Bytes20())
if evm.chainRules.IsEIP158 {
// if empty and transfers value

File diff suppressed because it is too large Load diff

View file

@ -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)
}
}
}

View file

@ -1,31 +0,0 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +build VERIFY_EVM_INTEGER_POOL
package vm
import "fmt"
const verifyPool = true
func verifyIntegerPool(ip *intPool) {
for i, item := range ip.pool.data {
if item.Cmp(checkVal) != 0 {
panic(fmt.Sprintf("%d'th item failed aggressive pool check. Value was modified", i))
}
}
}

View file

@ -1,23 +0,0 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +build !VERIFY_EVM_INTEGER_POOL
package vm
const verifyPool = false
func verifyIntegerPool(ip *intPool) {}

View file

@ -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, &copy); 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 = &copy
}
cfg.JumpTable = jt
cfg.ExtraEips = extraEips
}
return &EVMInterpreter{
@ -138,20 +143,13 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
// considered a revert-and-consume-all-gas operation except for
// ErrExecutionReverted which means revert-and-keep-gas-left.
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
if in.intPool == nil {
in.intPool = poolOfIntPools.get()
defer func() {
poolOfIntPools.put(in.intPool)
in.intPool = nil
}()
}
// Increment the call depth which is restricted to 1024
in.evm.depth++
defer func() { in.evm.depth-- }()
// Make sure the readOnly is only set if we aren't in readOnly yet.
// This makes also sure that the readOnly flag isn't removed for child calls.
// This also makes sure that the readOnly flag isn't removed for child calls.
if readOnly && !in.readOnly {
in.readOnly = true
defer func() { in.readOnly = false }()
@ -188,9 +186,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
)
contract.Input = input
// Reclaim the stack as an int pool when the execution stops
defer func() { in.intPool.put(stack.data...) }()
if in.cfg.Debug {
defer func() {
if err != nil {
@ -206,12 +201,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
// the execution of one of the operations or until the done flag is set by the
// parent context.
steps := 0
for {
steps++
if steps%1000 == 0 && atomic.LoadInt32(&in.evm.abort) != 0 {
break
}
if in.cfg.Debug {
// Capture pre-execution values for tracing.
logged, pcCopy, gasCopy = false, pc, contract.Gas
@ -221,26 +211,12 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// enough stack items available to perform the operation.
op = contract.GetOp(pc)
operation := in.cfg.JumpTable[op]
if !operation.valid {
return nil, &ErrInvalidOpCode{opcode: op}
}
// Validate stack
if sLen := stack.len(); sLen < operation.minStack {
return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
} else if sLen > operation.maxStack {
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
}
// If the operation is valid, enforce and write restrictions
if in.readOnly && in.evm.chainRules.IsByzantium {
// If the interpreter is operating in readonly mode, make sure no
// state-modifying operation is performed. The 3rd stack item
// for a call operation is the value. Transferring value from one
// account to the others means the state is modified and should also
// return with an error.
if operation.writes || (op == CALL && stack.Back(2).Sign() != 0) {
return nil, ErrWriteProtection
}
}
// Static portion of gas
cost = operation.constantGas // For tracing
if !contract.UseGas(operation.constantGas) {
@ -285,29 +261,17 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// execute the operation
res, err = operation.execute(&pc, in, callContext)
// verifyPool is a build flag. Pool verification makes sure the integrity
// of the integer pool by comparing values to a default value.
if verifyPool {
verifyIntegerPool(in.intPool)
}
// if the operation clears the return data (e.g. it has returning data)
// set the last return to the result of the operation.
if operation.returns {
in.returnData = res
}
switch {
case err != nil:
return nil, err
case operation.reverts:
log.Debug("ErrExecutionReverted", "pc", pc, "reverts", operation.reverts, "err", err)
return res, ErrExecutionReverted
case operation.halts:
return res, nil
case !operation.jumps:
pc++
if err != nil {
break
}
pc++
}
return nil, nil
if err == errStopToken {
err = nil // clear stop token error
}
return res, err
}
// CanRun tells if the contract, passed as an argument, can be

View file

@ -1,117 +0,0 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package vm
import (
"math/big"
"sync"
)
var checkVal = big.NewInt(-42)
const poolLimit = 256
// intPool is a pool of big integers that
// can be reused for all big.Int operations.
type intPool struct {
pool *Stack
}
func newIntPool() *intPool {
return &intPool{pool: newstack()}
}
// get retrieves a big int from the pool, allocating one if the pool is empty.
// Note, the returned int's value is arbitrary and will not be zeroed!
func (p *intPool) get() *big.Int {
if p.pool.len() > 0 {
return p.pool.pop()
}
return new(big.Int)
}
// getZero retrieves a big int from the pool, setting it to zero or allocating
// a new one if the pool is empty.
func (p *intPool) getZero() *big.Int {
if p.pool.len() > 0 {
return p.pool.pop().SetUint64(0)
}
return new(big.Int)
}
// putOne returns an allocated big int to the pool to be later reused by get calls.
// Note, the values as saved as is; neither put nor get zeroes the ints out!
// As opposed to 'put' with variadic args, this method becomes inlined by the
// go compiler
func (p *intPool) putOne(i *big.Int) {
if len(p.pool.data) > poolLimit {
return
}
p.pool.push(i)
}
// put returns an allocated big int to the pool to be later reused by get calls.
// Note, the values as saved as is; neither put nor get zeroes the ints out!
func (p *intPool) put(is ...*big.Int) {
if len(p.pool.data) > poolLimit {
return
}
for _, i := range is {
// verifyPool is a build flag. Pool verification makes sure the integrity
// of the integer pool by comparing values to a default value.
if verifyPool {
i.Set(checkVal)
}
p.pool.push(i)
}
}
// The intPool pool's default capacity
const poolDefaultCap = 25
// intPoolPool manages a pool of intPools.
type intPoolPool struct {
pools []*intPool
lock sync.Mutex
}
var poolOfIntPools = &intPoolPool{
pools: make([]*intPool, 0, poolDefaultCap),
}
// get is looking for an available pool to return.
func (ipp *intPoolPool) get() *intPool {
ipp.lock.Lock()
defer ipp.lock.Unlock()
if len(poolOfIntPools.pools) > 0 {
ip := ipp.pools[len(ipp.pools)-1]
ipp.pools = ipp.pools[:len(ipp.pools)-1]
return ip
}
return newIntPool()
}
// put a pool that has been allocated with get.
func (ipp *intPoolPool) put(ip *intPool) {
ipp.lock.Lock()
defer ipp.lock.Unlock()
if len(ipp.pools) < cap(ipp.pools) {
ipp.pools = append(ipp.pools, ip)
}
}

View file

@ -1,55 +0,0 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package vm
import (
"testing"
)
func TestIntPoolPoolGet(t *testing.T) {
poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
nip := poolOfIntPools.get()
if nip == nil {
t.Fatalf("Invalid pool allocation")
}
}
func TestIntPoolPoolPut(t *testing.T) {
poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
nip := poolOfIntPools.get()
if len(poolOfIntPools.pools) != 0 {
t.Fatalf("Pool got added to list when none should have been")
}
poolOfIntPools.put(nip)
if len(poolOfIntPools.pools) == 0 {
t.Fatalf("Pool did not get added to list when one should have been")
}
}
func TestIntPoolPoolReUse(t *testing.T) {
poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
nip := poolOfIntPools.get()
poolOfIntPools.put(nip)
poolOfIntPools.get()
if len(poolOfIntPools.pools) != 0 {
t.Fatalf("Invalid number of pools. Got %d, expected %d", len(poolOfIntPools.pools), 0)
}
}

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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})
}

View file

@ -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 {

View file

@ -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("####################")
}

View file

@ -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))
}

View file

@ -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,
}

View file

@ -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]
}

View file

@ -59,7 +59,7 @@
"result": {
"calls": [
{
"error": "invalid opcode: opcode 0xfe not defined",
"error": "invalid opcode: INVALID",
"from": "0x33056b5dcac09a9b4becad0e1dcf92c19bd0af76",
"gas": "0x75fe3",
"gasUsed": "0x75fe3",

View file

@ -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

View file

@ -40,7 +40,6 @@ func (account) SetBalance(*big.Int) {}
func (account) SetNonce(uint64) {}
func (account) Balance() *big.Int { return nil }
func (account) Address() common.Address { return common.Address{} }
func (account) ReturnGas(*big.Int) {}
func (account) SetCode(common.Hash, []byte) {}
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}

2
go.mod
View file

@ -20,6 +20,7 @@ require (
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb
github.com/gorilla/websocket v1.4.2
github.com/hashicorp/golang-lru v0.5.3
github.com/holiman/uint256 v1.2.4
github.com/huin/goupnp v1.3.0
github.com/influxdata/influxdb v1.7.9
github.com/jackpal/go-nat-pmp v1.0.2
@ -61,7 +62,6 @@ require (
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/holiman/uint256 v1.2.3 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e // indirect

2
go.sum
View file

@ -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=

View file

@ -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.

View file

@ -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,

View file

@ -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.

View file

@ -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")

View file

@ -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),
}
}

View file

@ -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.

View file

@ -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
)

View file

@ -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,
}
}

View file

@ -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)
}