mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-26 08:26:20 +00:00
Merge pull request #536 from XinFinOrg/dev-upgrade
Monthly Merge Dev-Upgrade to Master Branch: May
This commit is contained in:
commit
f041ad5a9b
185 changed files with 12957 additions and 3540 deletions
21
.github/workflows/ci.yml
vendored
21
.github/workflows/ci.yml
vendored
|
|
@ -80,21 +80,27 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/')
|
||||
needs: tests
|
||||
outputs:
|
||||
output1: ${{ steps.docker.outputs.image_name }}
|
||||
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
|
||||
id: docker
|
||||
run: |
|
||||
git_hash=$(git rev-parse --short "$GITHUB_SHA")
|
||||
image_name=xinfinorg/devnet:dev-upgrade-${git_hash}
|
||||
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 tag xinfinorg/devnet:latest xinfinorg/devnet:dev-upgrade-${git_hash}
|
||||
docker push xinfinorg/devnet:dev-upgrade-${git_hash}
|
||||
docker tag xinfinorg/devnet:latest $image_name
|
||||
docker push $image_name
|
||||
docker push xinfinorg/devnet:latest
|
||||
docker push xinfinorg/devnet:previous
|
||||
echo "image_name=$image_name"
|
||||
echo "image_name=$image_name" >> "$GITHUB_OUTPUT"
|
||||
|
||||
devnet_terraform_apply:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -142,6 +148,17 @@ jobs:
|
|||
terraform init ${{ env.tf_init_cli_options }}
|
||||
terraform apply ${{ env.tf_apply_cli_options }}
|
||||
|
||||
- name: Update RPC nodes image
|
||||
uses: dawidd6/action-ansible-playbook@v2
|
||||
with:
|
||||
playbook: playbooks/update-image.yaml
|
||||
directory: ./cicd/ansible
|
||||
key: ${{secrets.SSH_PRIVATE_KEY_DEVNET}}
|
||||
options: |
|
||||
--inventory inventory.yaml
|
||||
--extra-vars network=ec2_rpcs
|
||||
--extra-vars rpc_image=${{ needs.devnet_build_push.outputs.output1 }}
|
||||
|
||||
devnet_dev-upgrade_node:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/')
|
||||
|
|
|
|||
41
.github/workflows/deploy_rpc_image.yml
vendored
Normal file
41
.github/workflows/deploy_rpc_image.yml
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
name: Deploy RPC Image
|
||||
on:
|
||||
#need to make sure only authorized people can use this function
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
network:
|
||||
type: choice
|
||||
description: 'devnet, testnet, or mainnet'
|
||||
options:
|
||||
- devnet
|
||||
- testnet
|
||||
- mainnet
|
||||
rpc_image:
|
||||
description: 'full image name'
|
||||
|
||||
jobs:
|
||||
ansible:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Update RPC nodes image
|
||||
uses: dawidd6/action-ansible-playbook@v2
|
||||
with:
|
||||
playbook: playbooks/update-image.yaml
|
||||
directory: ./cicd/ansible
|
||||
key: ${{secrets.SSH_PRIVATE_KEY_DEVNET}}
|
||||
options: |
|
||||
--inventory inventory.yaml
|
||||
--extra-vars network=${{inputs.network}}
|
||||
--extra-vars rpc_image=${{inputs.rpc_image}}
|
||||
|
||||
devnet_send_notification:
|
||||
runs-on: ubuntu-latest
|
||||
needs: ansible
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Send deployment notification
|
||||
run: |
|
||||
curl --location --request POST "66.94.98.186:8080/deploy?environment=${{inputs.network}}&service=xdc_rpc&version=${{inputs.rpc_image}}"
|
||||
|
||||
11
XDCx/XDCx.go
11
XDCx/XDCx.go
|
|
@ -9,14 +9,13 @@ import (
|
|||
|
||||
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
|
||||
"github.com/XinFinOrg/XDPoSChain/XDCxDAO"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/p2p"
|
||||
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/prque"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/p2p"
|
||||
"github.com/XinFinOrg/XDPoSChain/rpc"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"golang.org/x/sync/syncmap"
|
||||
|
|
@ -105,7 +104,7 @@ func New(cfg *Config) *XDCX {
|
|||
}
|
||||
XDCX := &XDCX{
|
||||
orderNonce: make(map[common.Address]*big.Int),
|
||||
Triegc: prque.New(),
|
||||
Triegc: prque.New(nil),
|
||||
tokenDecimalCache: tokenDecimalCache,
|
||||
orderCache: orderCache,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,14 +12,13 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
|
||||
"github.com/XinFinOrg/XDPoSChain/XDCxDAO"
|
||||
"github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/p2p"
|
||||
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/prque"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/p2p"
|
||||
"github.com/XinFinOrg/XDPoSChain/rpc"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
|
@ -67,7 +66,7 @@ func New(XDCx *XDCx.XDCX) *Lending {
|
|||
lendingTradeCache, _ := lru.New(defaultCacheLimit)
|
||||
lending := &Lending{
|
||||
orderNonce: make(map[common.Address]*big.Int),
|
||||
Triegc: prque.New(),
|
||||
Triegc: prque.New(nil),
|
||||
lendingItemHistory: itemCache,
|
||||
lendingTradeHistory: lendingTradeCache,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,11 +25,9 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain"
|
||||
"github.com/XinFinOrg/XDPoSChain/XDCx"
|
||||
"github.com/XinFinOrg/XDPoSChain/XDCxlending"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain"
|
||||
"github.com/XinFinOrg/XDPoSChain/accounts"
|
||||
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
|
||||
"github.com/XinFinOrg/XDPoSChain/accounts/keystore"
|
||||
|
|
@ -37,10 +35,10 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/bloombits"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||
|
|
@ -365,7 +363,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.Cal
|
|||
from := statedb.GetOrNewStateObject(call.From)
|
||||
from.SetBalance(math.MaxBig256)
|
||||
// Execute the call.
|
||||
msg := callmsg{call}
|
||||
msg := callMsg{call}
|
||||
feeCapacity := state.GetTRC21FeeCapacityFromState(statedb)
|
||||
if msg.To() != nil {
|
||||
if value, ok := feeCapacity[*msg.To()]; ok {
|
||||
|
|
@ -388,7 +386,10 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
|
|||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
sender, err := types.Sender(types.HomesteadSigner{}, tx)
|
||||
// Check transaction validity.
|
||||
block := b.blockchain.CurrentBlock()
|
||||
signer := types.MakeSigner(b.blockchain.Config(), block.Number())
|
||||
sender, err := types.Sender(signer, tx)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("invalid transaction: %v", err))
|
||||
}
|
||||
|
|
@ -397,7 +398,8 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
|
|||
panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce))
|
||||
}
|
||||
|
||||
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), b.blockchain.Engine(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||
// Include tx in chain.
|
||||
blocks, _ := core.GenerateChain(b.config, block, b.blockchain.Engine(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||
for _, tx := range b.pendingBlock.Transactions() {
|
||||
block.AddTxWithChain(b.blockchain, tx)
|
||||
}
|
||||
|
|
@ -501,20 +503,21 @@ func (b *SimulatedBackend) GetBlockChain() *core.BlockChain {
|
|||
return b.blockchain
|
||||
}
|
||||
|
||||
// callmsg implements core.Message to allow passing it as a transaction simulator.
|
||||
type callmsg struct {
|
||||
// callMsg implements core.Message to allow passing it as a transaction simulator.
|
||||
type callMsg struct {
|
||||
XDPoSChain.CallMsg
|
||||
}
|
||||
|
||||
func (m callmsg) From() common.Address { return m.CallMsg.From }
|
||||
func (m callmsg) Nonce() uint64 { return 0 }
|
||||
func (m callmsg) CheckNonce() bool { return false }
|
||||
func (m callmsg) To() *common.Address { return m.CallMsg.To }
|
||||
func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
||||
func (m callmsg) Gas() uint64 { return m.CallMsg.Gas }
|
||||
func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
|
||||
func (m callmsg) Data() []byte { return m.CallMsg.Data }
|
||||
func (m callmsg) BalanceTokenFee() *big.Int { return m.CallMsg.BalanceTokenFee }
|
||||
func (m callMsg) From() common.Address { return m.CallMsg.From }
|
||||
func (m callMsg) Nonce() uint64 { return 0 }
|
||||
func (m callMsg) CheckNonce() bool { return false }
|
||||
func (m callMsg) To() *common.Address { return m.CallMsg.To }
|
||||
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
||||
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
|
||||
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
|
||||
func (m callMsg) Data() []byte { return m.CallMsg.Data }
|
||||
func (m callMsg) BalanceTokenFee() *big.Int { return m.CallMsg.BalanceTokenFee }
|
||||
func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList }
|
||||
|
||||
// filterBackend implements filters.Backend to support filtering for logs without
|
||||
// taking bloom-bits acceleration structures into account.
|
||||
|
|
@ -553,7 +556,7 @@ func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*ty
|
|||
return logs, nil
|
||||
}
|
||||
|
||||
func (fb *filterBackend) SubscribeTxPreEvent(ch chan<- core.TxPreEvent) event.Subscription {
|
||||
func (fb *filterBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||
<-quit
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -18,12 +18,14 @@
|
|||
package accounts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
ethereum "github.com/XinFinOrg/XDPoSChain"
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/event"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// Account represents an Ethereum account located at a specific location defined
|
||||
|
|
@ -148,6 +150,34 @@ type Backend interface {
|
|||
Subscribe(sink chan<- WalletEvent) event.Subscription
|
||||
}
|
||||
|
||||
// TextHash is a helper function that calculates a hash for the given message that can be
|
||||
// safely used to calculate a signature from.
|
||||
//
|
||||
// The hash is calulcated as
|
||||
//
|
||||
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
|
||||
//
|
||||
// This gives context to the signed message and prevents signing of transactions.
|
||||
func TextHash(data []byte) []byte {
|
||||
hash, _ := TextAndHash(data)
|
||||
return hash
|
||||
}
|
||||
|
||||
// TextAndHash is a helper function that calculates a hash for the given message that can be
|
||||
// safely used to calculate a signature from.
|
||||
//
|
||||
// The hash is calulcated as
|
||||
//
|
||||
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
|
||||
//
|
||||
// This gives context to the signed message and prevents signing of transactions.
|
||||
func TextAndHash(data []byte) ([]byte, string) {
|
||||
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), string(data))
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
hasher.Write([]byte(msg))
|
||||
return hasher.Sum(nil), msg
|
||||
}
|
||||
|
||||
// WalletEventType represents the different event types that can be fired by
|
||||
// the wallet subscription subsystem.
|
||||
type WalletEventType int
|
||||
|
|
|
|||
|
|
@ -288,11 +288,9 @@ func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *b
|
|||
if !found {
|
||||
return nil, ErrLocked
|
||||
}
|
||||
// Depending on the presence of the chain ID, sign with EIP155 or homestead
|
||||
if chainID != nil {
|
||||
return types.SignTx(tx, types.NewEIP155Signer(chainID), unlockedKey.PrivateKey)
|
||||
}
|
||||
return types.SignTx(tx, types.HomesteadSigner{}, unlockedKey.PrivateKey)
|
||||
// Depending on the presence of the chain ID, sign with 2718 or homestead
|
||||
signer := types.LatestSignerForChainID(chainID)
|
||||
return types.SignTx(tx, signer, unlockedKey.PrivateKey)
|
||||
}
|
||||
|
||||
// SignHashWithPassphrase signs hash if the private key matching the given address
|
||||
|
|
@ -316,11 +314,9 @@ func (ks *KeyStore) SignTxWithPassphrase(a accounts.Account, passphrase string,
|
|||
}
|
||||
defer zeroKey(key.PrivateKey)
|
||||
|
||||
// Depending on the presence of the chain ID, sign with EIP155 or homestead
|
||||
if chainID != nil {
|
||||
return types.SignTx(tx, types.NewEIP155Signer(chainID), key.PrivateKey)
|
||||
}
|
||||
return types.SignTx(tx, types.HomesteadSigner{}, key.PrivateKey)
|
||||
// Depending on the presence of the chain ID, sign with or without replay protection.
|
||||
signer := types.LatestSignerForChainID(chainID)
|
||||
return types.SignTx(tx, signer, key.PrivateKey)
|
||||
}
|
||||
|
||||
// Unlock unlocks the given account indefinitely.
|
||||
|
|
|
|||
|
|
@ -27,13 +27,13 @@ import (
|
|||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/XinFinOrg/XDPoSChain/accounts"
|
||||
"github.com/XinFinOrg/XDPoSChain/accounts/usbwallet/internal/trezor"
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
// ErrTrezorPINNeeded is returned if opening the trezor requires a PIN code. In
|
||||
|
|
@ -80,13 +80,13 @@ func (w *trezorDriver) Status() (string, error) {
|
|||
|
||||
// Open implements usbwallet.driver, attempting to initialize the connection to
|
||||
// the Trezor hardware wallet. Initializing the Trezor is a two phase operation:
|
||||
// * The first phase is to initialize the connection and read the wallet's
|
||||
// features. This phase is invoked is the provided passphrase is empty. The
|
||||
// device will display the pinpad as a result and will return an appropriate
|
||||
// error to notify the user that a second open phase is needed.
|
||||
// * The second phase is to unlock access to the Trezor, which is done by the
|
||||
// user actually providing a passphrase mapping a keyboard keypad to the pin
|
||||
// number of the user (shuffled according to the pinpad displayed).
|
||||
// - The first phase is to initialize the connection and read the wallet's
|
||||
// features. This phase is invoked is the provided passphrase is empty. The
|
||||
// device will display the pinpad as a result and will return an appropriate
|
||||
// error to notify the user that a second open phase is needed.
|
||||
// - The second phase is to unlock access to the Trezor, which is done by the
|
||||
// user actually providing a passphrase mapping a keyboard keypad to the pin
|
||||
// number of the user (shuffled according to the pinpad displayed).
|
||||
func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
|
||||
w.device, w.failure = device, nil
|
||||
|
||||
|
|
@ -220,9 +220,11 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
|
|||
if chainID == nil {
|
||||
signer = new(types.HomesteadSigner)
|
||||
} else {
|
||||
// Trezor backend does not support typed transactions yet.
|
||||
signer = types.NewEIP155Signer(chainID)
|
||||
signature[64] = signature[64] - byte(chainID.Uint64()*2+35)
|
||||
}
|
||||
|
||||
// Inject the final signature into the transaction and sanity check the sender
|
||||
signed, err := tx.WithSignature(signer, signature)
|
||||
if err != nil {
|
||||
|
|
|
|||
17
cicd/ansible/inventory.yaml
Normal file
17
cicd/ansible/inventory.yaml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
ec2_rpcs:
|
||||
hosts:
|
||||
devnet:
|
||||
ansible_host: devnet.hashlabs.apothem.network
|
||||
ansible_port: 22
|
||||
ansible_user: ec2-user
|
||||
deploy_path: /work/XinFin-Node/devnet
|
||||
testnet:
|
||||
ansible_host: testnet.hashlabs.apothem.network
|
||||
ansible_port: 22
|
||||
ansible_user: ec2-user
|
||||
deploy_path: /work/XinFin-Node/testnet
|
||||
mainnet:
|
||||
ansible_host: mainnet.hashlabs.apothem.network
|
||||
ansible_port: 22
|
||||
ansible_user: ec2-user
|
||||
deploy_path: /work/XinFin-Node/mainnet
|
||||
15
cicd/ansible/playbooks/update-image.yaml
Normal file
15
cicd/ansible/playbooks/update-image.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
- name: Run Bash Script on Host
|
||||
hosts: "{{ network }}"
|
||||
become: true #sudo/root
|
||||
|
||||
tasks:
|
||||
- name: Update RPC image version
|
||||
shell: |
|
||||
export RPC_IMAGE={{ rpc_image }}
|
||||
cd {{ deploy_path }}
|
||||
./docker-down.sh
|
||||
./docker-up-hash.sh
|
||||
docker ps
|
||||
register: output
|
||||
- debug: var=output.stdout_lines
|
||||
|
|
@ -81,5 +81,5 @@ XDC --ethstats ${netstats} --gcmode archive \
|
|||
--rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \
|
||||
--gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \
|
||||
--debugdatadir /work/xdcchain \
|
||||
--enable-0x-prefix --ws --wsaddr=0.0.0.0 --wsport $ws_port \
|
||||
--ws --wsaddr=0.0.0.0 --wsport $ws_port \
|
||||
--wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log
|
||||
|
|
|
|||
|
|
@ -80,5 +80,5 @@ XDC --ethstats ${netstats} --gcmode archive \
|
|||
--rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \
|
||||
--gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \
|
||||
--debugdatadir /work/xdcchain \
|
||||
--enable-0x-prefix --ws --wsaddr=0.0.0.0 --wsport $ws_port \
|
||||
--ws --wsaddr=0.0.0.0 --wsport $ws_port \
|
||||
--wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log
|
||||
|
|
|
|||
|
|
@ -10,4 +10,4 @@ eu_west_1_end=72
|
|||
|
||||
# Sydney
|
||||
ap_southeast_2_start=73
|
||||
ap_southeast_2_end=108
|
||||
ap_southeast_2_end=108
|
||||
|
|
|
|||
|
|
@ -76,3 +76,48 @@ module "mainnet-rpc" {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
module "devnet_rpc" {
|
||||
source = "./module/ec2_rpc"
|
||||
network = "devnet"
|
||||
vpc_id = local.vpc_id
|
||||
aws_subnet_id = local.aws_subnet_id
|
||||
ami_id = local.ami_id
|
||||
instance_type = "t3.large"
|
||||
ssh_key_name = local.ssh_key_name
|
||||
rpc_image = local.rpc_image
|
||||
|
||||
providers = {
|
||||
aws = aws.ap-southeast-1
|
||||
}
|
||||
}
|
||||
|
||||
module "testnet_rpc" {
|
||||
source = "./module/ec2_rpc"
|
||||
network = "testnet"
|
||||
vpc_id = local.vpc_id
|
||||
aws_subnet_id = local.aws_subnet_id
|
||||
ami_id = local.ami_id
|
||||
instance_type = "t3.large"
|
||||
ssh_key_name = local.ssh_key_name
|
||||
rpc_image = local.rpc_image
|
||||
|
||||
providers = {
|
||||
aws = aws.ap-southeast-1
|
||||
}
|
||||
}
|
||||
|
||||
module "mainnet_rpc" {
|
||||
source = "./module/ec2_rpc"
|
||||
network = "mainnet"
|
||||
vpc_id = local.vpc_id
|
||||
aws_subnet_id = local.aws_subnet_id
|
||||
ami_id = local.ami_id
|
||||
instance_type = "t3.large"
|
||||
ssh_key_name = local.ssh_key_name
|
||||
rpc_image = local.rpc_image
|
||||
|
||||
providers = {
|
||||
aws = aws.ap-southeast-1
|
||||
}
|
||||
}
|
||||
106
cicd/terraform/module/ec2_rpc/main.tf
Normal file
106
cicd/terraform/module/ec2_rpc/main.tf
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
terraform {
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.13.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
variable network {
|
||||
type = string
|
||||
}
|
||||
variable vpc_id {
|
||||
type = string
|
||||
}
|
||||
variable aws_subnet_id {
|
||||
type = string
|
||||
}
|
||||
variable ami_id {
|
||||
type = string
|
||||
}
|
||||
variable instance_type {
|
||||
type = string
|
||||
}
|
||||
variable ssh_key_name {
|
||||
type = string
|
||||
}
|
||||
variable rpc_image {
|
||||
type = string
|
||||
}
|
||||
|
||||
resource "aws_security_group" "rpc_sg" {
|
||||
name_prefix = "${var.network}_rpc_sg"
|
||||
|
||||
ingress {
|
||||
from_port = 22
|
||||
to_port = 22
|
||||
protocol = "tcp"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
ingress {
|
||||
from_port = 30303
|
||||
to_port = 30303
|
||||
protocol = "tcp"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
ingress {
|
||||
from_port = 8545
|
||||
to_port = 8545
|
||||
protocol = "tcp"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
ingress {
|
||||
from_port = 8555
|
||||
to_port = 8555
|
||||
protocol = "tcp"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
egress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_instance" "rpc_instance" {
|
||||
instance_type = var.instance_type
|
||||
ami = var.ami_id
|
||||
tags = {
|
||||
Name = var.network
|
||||
}
|
||||
key_name = var.ssh_key_name
|
||||
vpc_security_group_ids = [aws_security_group.rpc_sg.id]
|
||||
ebs_block_device {
|
||||
device_name = "/dev/xvda"
|
||||
volume_size = 500
|
||||
}
|
||||
|
||||
|
||||
#below still need to remove git checkout {{branch}} after files merged to master
|
||||
user_data = <<-EOF
|
||||
#!/bin/bash
|
||||
sudo yum update -y
|
||||
sudo yum upgrade -y
|
||||
sudo yum install git -y
|
||||
sudo yum install docker -y
|
||||
mkdir -p /root/.docker/cli-plugins
|
||||
curl -SL https://github.com/docker/compose/releases/download/v2.25.0/docker-compose-linux-x86_64 -o /root/.docker/cli-plugins/docker-compose
|
||||
sudo chmod +x /root/.docker/cli-plugins/docker-compose
|
||||
echo checking compose version
|
||||
docker compose version
|
||||
sudo systemctl enable docker
|
||||
sudo systemctl start docker
|
||||
mkdir -p /work
|
||||
cd /work
|
||||
git clone https://github.com/XinFinOrg/XinFin-Node
|
||||
cd /work/XinFin-Node/${var.network}
|
||||
export RPC_IMAGE="${var.rpc_image}"
|
||||
echo RPC_IMAGE=$RPC_IMAGE
|
||||
./docker-up-hash.sh
|
||||
EOF
|
||||
}
|
||||
|
|
@ -34,3 +34,11 @@ locals {
|
|||
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
|
||||
}
|
||||
|
||||
locals { //ec2_rpc values
|
||||
ami_id = "ami-097c4e1feeea169e5"
|
||||
rpc_image = "xinfinorg/xdposchain:v2.2.0-beta1"
|
||||
vpc_id = "vpc-20a06846"
|
||||
aws_subnet_id = "subnet-4653ee20"
|
||||
ssh_key_name = "devnetkey"
|
||||
}
|
||||
|
|
@ -82,5 +82,5 @@ XDC --ethstats ${netstats} --gcmode archive \
|
|||
--rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \
|
||||
--gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \
|
||||
--debugdatadir /work/xdcchain \
|
||||
--enable-0x-prefix --ws --wsaddr=0.0.0.0 --wsport $ws_port \
|
||||
--ws --wsaddr=0.0.0.0 --wsport $ws_port \
|
||||
--wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log
|
||||
|
|
|
|||
|
|
@ -164,8 +164,8 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) {
|
|||
common.TIPXDCXCancellationFee = common.TIPXDCXCancellationFeeTestnet
|
||||
}
|
||||
|
||||
if ctx.GlobalBool(utils.Enable0xPrefixFlag.Name) {
|
||||
common.Enable0xPrefix = true
|
||||
if ctx.GlobalBool(utils.EnableXDCPrefixFlag.Name) {
|
||||
common.Enable0xPrefix = false
|
||||
}
|
||||
|
||||
// Rewound
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ const (
|
|||
// Tests that a node embedded within a console can be started up properly and
|
||||
// then terminated by closing the input stream.
|
||||
func TestConsoleWelcome(t *testing.T) {
|
||||
coinbase := "xdc8605cdbbdb6d264aa742e77020dcbc58fcdce182"
|
||||
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
|
||||
|
||||
// Start a XDC console, make sure it's cleaned up and terminate the console
|
||||
XDC := runXDC(t,
|
||||
|
|
@ -75,7 +75,7 @@ at block: 0 ({{niltime}})
|
|||
// Tests that a console can be attached to a running node via various means.
|
||||
func TestIPCAttachWelcome(t *testing.T) {
|
||||
// Configure the instance for IPC attachement
|
||||
coinbase := "xdc8605cdbbdb6d264aa742e77020dcbc58fcdce182"
|
||||
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
|
||||
var ipc string
|
||||
if runtime.GOOS == "windows" {
|
||||
ipc = `\\.\pipe\XDC` + strconv.Itoa(trulyRandInt(100000, 999999))
|
||||
|
|
@ -97,7 +97,7 @@ func TestIPCAttachWelcome(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestHTTPAttachWelcome(t *testing.T) {
|
||||
coinbase := "xdc8605cdbbdb6d264aa742e77020dcbc58fcdce182"
|
||||
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
|
||||
port := strconv.Itoa(trulyRandInt(1024, 65536)) // Yeah, sometimes this will fail, sorry :P
|
||||
XDC := runXDC(t,
|
||||
"--XDCx.datadir", tmpdir(t)+"XDCx/"+time.Now().String(),
|
||||
|
|
@ -112,7 +112,7 @@ func TestHTTPAttachWelcome(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestWSAttachWelcome(t *testing.T) {
|
||||
coinbase := "xdc8605cdbbdb6d264aa742e77020dcbc58fcdce182"
|
||||
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
|
||||
port := strconv.Itoa(trulyRandInt(1024, 65536)) // Yeah, sometimes this will fail, sorry :P
|
||||
|
||||
XDC := runXDC(t,
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ var (
|
|||
//utils.VMEnableDebugFlag,
|
||||
utils.XDCTestnetFlag,
|
||||
utils.Enable0xPrefixFlag,
|
||||
utils.EnableXDCPrefixFlag,
|
||||
utils.RewoundFlag,
|
||||
utils.NetworkIdFlag,
|
||||
utils.RPCCORSDomainFlag,
|
||||
|
|
|
|||
|
|
@ -33,15 +33,23 @@ type JSONLogger struct {
|
|||
}
|
||||
|
||||
func NewJSONLogger(cfg *vm.LogConfig, writer io.Writer) *JSONLogger {
|
||||
return &JSONLogger{json.NewEncoder(writer), cfg}
|
||||
l := &JSONLogger{json.NewEncoder(writer), cfg}
|
||||
if l.cfg == nil {
|
||||
l.cfg = &vm.LogConfig{}
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
|
||||
return nil
|
||||
func (l *JSONLogger) CaptureStart(env *vm.EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||
}
|
||||
|
||||
func (l *JSONLogger) CaptureFault(*vm.EVM, uint64, vm.OpCode, uint64, uint64, *vm.ScopeContext, int, error) {
|
||||
}
|
||||
|
||||
// CaptureState outputs state information on the logger.
|
||||
func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
||||
func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
memory := scope.Memory
|
||||
stack := scope.Stack
|
||||
log := vm.StructLog{
|
||||
Pc: pc,
|
||||
Op: op,
|
||||
|
|
@ -63,24 +71,20 @@ func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cos
|
|||
}
|
||||
log.Stack = logstack
|
||||
}
|
||||
return l.encoder.Encode(log)
|
||||
}
|
||||
|
||||
// CaptureFault outputs state information on the logger.
|
||||
func (l *JSONLogger) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
||||
return nil
|
||||
l.encoder.Encode(log)
|
||||
}
|
||||
|
||||
// CaptureEnd is triggered at end of execution.
|
||||
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
|
||||
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
|
||||
type endLog struct {
|
||||
Output string `json:"output"`
|
||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||
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, ""})
|
||||
l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, errMsg})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,8 +145,8 @@ func (w *wizard) makeGenesis() {
|
|||
genesis.Config.XDPoS.V2.CurrentConfig.TimeoutSyncThreshold = w.readDefaultInt(3)
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("How many v2 vote collection to generate a QC, should be two thirds of masternodes? (default = %f)\n", 0.666)
|
||||
genesis.Config.XDPoS.V2.CurrentConfig.CertThreshold = w.readDefaultFloat(0.666)
|
||||
fmt.Printf("Proportion of total masternodes v2 vote collection to generate a QC (float value), should be two thirds of masternodes? (default = %f)\n", 0.667)
|
||||
genesis.Config.XDPoS.V2.CurrentConfig.CertThreshold = w.readDefaultFloat(0.667)
|
||||
|
||||
genesis.Config.XDPoS.V2.AllConfigs[0] = genesis.Config.XDPoS.V2.CurrentConfig
|
||||
|
||||
|
|
|
|||
|
|
@ -117,7 +117,11 @@ var (
|
|||
}
|
||||
Enable0xPrefixFlag = cli.BoolFlag{
|
||||
Name: "enable-0x-prefix",
|
||||
Usage: "Addres use 0x-prefix (default = false)",
|
||||
Usage: "Addres use 0x-prefix (Deprecated: this is on by default, to use xdc prefix use --enable-xdc-prefix)",
|
||||
}
|
||||
EnableXDCPrefixFlag = cli.BoolFlag{
|
||||
Name: "enable-xdc-prefix",
|
||||
Usage: "Addres use xdc-prefix (default = false)",
|
||||
}
|
||||
// General settings
|
||||
AnnounceTxsFlag = cli.BoolFlag{
|
||||
|
|
@ -354,6 +358,11 @@ var (
|
|||
Name: "vmdebug",
|
||||
Usage: "Record information useful for VM and contract debugging",
|
||||
}
|
||||
RPCGlobalGasCapFlag = cli.Uint64Flag{
|
||||
Name: "rpc.gascap",
|
||||
Usage: "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)",
|
||||
Value: eth.DefaultConfig.RPCGasCap,
|
||||
}
|
||||
// Logging and debug settings
|
||||
EthStatsURLFlag = cli.StringFlag{
|
||||
Name: "ethstats",
|
||||
|
|
@ -786,6 +795,10 @@ func setIPC(ctx *cli.Context, cfg *node.Config) {
|
|||
}
|
||||
}
|
||||
|
||||
func setPrefix(ctx *cli.Context, cfg *node.Config) {
|
||||
checkExclusive(ctx, Enable0xPrefixFlag, EnableXDCPrefixFlag)
|
||||
}
|
||||
|
||||
// MakeDatabaseHandles raises out the number of allowed file handles per process
|
||||
// for XDC and returns half of the allowance to assign to the database.
|
||||
func MakeDatabaseHandles() int {
|
||||
|
|
@ -933,6 +946,7 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
|
|||
setHTTP(ctx, cfg)
|
||||
setWS(ctx, cfg)
|
||||
setNodeUserIdent(ctx, cfg)
|
||||
setPrefix(ctx, cfg)
|
||||
|
||||
switch {
|
||||
case ctx.GlobalIsSet(DataDirFlag.Name):
|
||||
|
|
@ -1167,6 +1181,11 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
|||
// TODO(fjl): force-enable this in --dev mode
|
||||
cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name)
|
||||
}
|
||||
if cfg.RPCGasCap != 0 {
|
||||
log.Info("Set global gas cap", "cap", cfg.RPCGasCap)
|
||||
} else {
|
||||
log.Info("Global gas cap disabled")
|
||||
}
|
||||
if ctx.GlobalIsSet(StoreRewardFlag.Name) {
|
||||
common.StoreRewardFolder = filepath.Join(stack.DataDir(), "XDC", "rewards")
|
||||
if _, err := os.Stat(common.StoreRewardFolder); os.IsNotExist(err) {
|
||||
|
|
|
|||
|
|
@ -139,8 +139,8 @@ func processArgs() {
|
|||
}
|
||||
|
||||
if *asymmetricMode && len(*argPub) > 0 {
|
||||
pub = crypto.ToECDSAPub(common.FromHex(*argPub))
|
||||
if !isKeyValid(pub) {
|
||||
var err error
|
||||
if pub, err = crypto.UnmarshalPubkey(common.FromHex(*argPub)); err != nil {
|
||||
utils.Fatalf("invalid public key")
|
||||
}
|
||||
}
|
||||
|
|
@ -337,9 +337,8 @@ func configureNode() {
|
|||
if b == nil {
|
||||
utils.Fatalf("Error: can not convert hexadecimal string")
|
||||
}
|
||||
pub = crypto.ToECDSAPub(b)
|
||||
if !isKeyValid(pub) {
|
||||
utils.Fatalf("Error: invalid public key")
|
||||
if pub, err = crypto.UnmarshalPubkey(b); err != nil {
|
||||
utils.Fatalf("Error: invalid peer public key")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@ var TIPXDCX = big.NewInt(38383838)
|
|||
var TIPXDCXLending = big.NewInt(38383838)
|
||||
var TIPXDCXCancellationFee = big.NewInt(38383838)
|
||||
var TIPXDCXCancellationFeeTestnet = big.NewInt(38383838)
|
||||
var TIPXDCXMinerDisable = big.NewInt(88999999900)
|
||||
var TIPXDCXReceiverDisable = big.NewInt(99999999999)
|
||||
var Eip1559Block = big.NewInt(9999999999)
|
||||
var TIPXDCXDISABLE = big.NewInt(99999999900)
|
||||
var BerlinBlock = big.NewInt(76321000) // Target 19th June 2024
|
||||
var LondonBlock = big.NewInt(76321000) // Target 19th June 2024
|
||||
|
|
@ -53,7 +56,7 @@ var ShanghaiBlock = big.NewInt(76321000) // Target 19th June 2024
|
|||
|
||||
var TIPXDCXTestnet = big.NewInt(38383838)
|
||||
var IsTestnet bool = false
|
||||
var Enable0xPrefix bool = false
|
||||
var Enable0xPrefix bool = true
|
||||
var StoreRewardFolder string
|
||||
var RollbackHash Hash
|
||||
var BasePrice = big.NewInt(1000000000000000000) // 1
|
||||
|
|
|
|||
|
|
@ -45,15 +45,17 @@ 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 TIPXDCXMinerDisable = big.NewInt(15894900)
|
||||
var TIPXDCXReceiverDisable = big.NewInt(18018000)
|
||||
var BerlinBlock = big.NewInt(16832700)
|
||||
var LondonBlock = big.NewInt(16832700)
|
||||
var MergeBlock = big.NewInt(16832700)
|
||||
var ShanghaiBlock = big.NewInt(16832700)
|
||||
var Eip1559Block = big.NewInt(9999999999)
|
||||
|
||||
var TIPXDCXTestnet = big.NewInt(0)
|
||||
var IsTestnet bool = false
|
||||
var Enable0xPrefix bool = false
|
||||
var Enable0xPrefix bool = true
|
||||
var StoreRewardFolder string
|
||||
var RollbackHash Hash
|
||||
var BasePrice = big.NewInt(1000000000000000000) // 1
|
||||
|
|
|
|||
|
|
@ -45,15 +45,17 @@ var TIPXDCX = big.NewInt(23779191)
|
|||
var TIPXDCXLending = big.NewInt(23779191)
|
||||
var TIPXDCXCancellationFee = big.NewInt(23779191)
|
||||
var TIPXDCXCancellationFeeTestnet = big.NewInt(23779191)
|
||||
var TIPXDCXDISABLE = big.NewInt(61290000) // Target 31st March 2024
|
||||
var BerlinBlock = big.NewInt(9999999999)
|
||||
var LondonBlock = big.NewInt(9999999999)
|
||||
var MergeBlock = big.NewInt(9999999999)
|
||||
var TIPXDCXMinerDisable = big.NewInt(61290000) // Target 31st March 2024
|
||||
var TIPXDCXReceiverDisable = big.NewInt(9999999999)
|
||||
var BerlinBlock = big.NewInt(61290000)
|
||||
var LondonBlock = big.NewInt(61290000)
|
||||
var MergeBlock = big.NewInt(61290000)
|
||||
var ShanghaiBlock = big.NewInt(61290000) // Target 31st March 2024
|
||||
var Eip1559Block = big.NewInt(9999999999)
|
||||
|
||||
var TIPXDCXTestnet = big.NewInt(23779191)
|
||||
var IsTestnet bool = false
|
||||
var Enable0xPrefix bool = false
|
||||
var Enable0xPrefix bool = true
|
||||
var StoreRewardFolder string
|
||||
var RollbackHash Hash
|
||||
var BasePrice = big.NewInt(1000000000000000000) // 1
|
||||
|
|
|
|||
|
|
@ -240,7 +240,7 @@ func (x *XDPoS) VerifyHeaders(chain consensus.ChainReader, headers []*types.Head
|
|||
func (x *XDPoS) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
|
||||
switch x.config.BlockConsensusVersion(block.Number(), block.Extra(), ExtraFieldCheck) {
|
||||
case params.ConsensusEngineVersion2:
|
||||
return nil
|
||||
return x.EngineV2.VerifyUncles(chain, block)
|
||||
default: // Default "v1"
|
||||
return x.EngineV1.VerifyUncles(chain, block)
|
||||
}
|
||||
|
|
@ -457,7 +457,7 @@ func (x *XDPoS) GetSnapshot(chain consensus.ChainReader, header *types.Header) (
|
|||
return &utils.PublicApiSnapshot{
|
||||
Number: sp.Number,
|
||||
Hash: sp.Hash,
|
||||
Signers: sp.GetMappedMasterNodes(),
|
||||
Signers: sp.GetMappedCandidates(),
|
||||
}, err
|
||||
default: // Default "v1"
|
||||
sp, err := x.EngineV1.GetSnapshot(chain, header)
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ func (x *XDPoS_v1) verifyHeaderWithCache(chain consensus.ChainReader, header *ty
|
|||
// looking those up from the database. This is useful for concurrently verifying
|
||||
// a batch of new headers.
|
||||
func (x *XDPoS_v1) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error {
|
||||
fullVerify = false
|
||||
// If we're running a engine faking, accept any block as valid
|
||||
if x.config.SkipV1Validation {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package engine_v2
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
|
|
@ -468,7 +469,7 @@ func (x *XDPoS_v2) IsAuthorisedAddress(chain consensus.ChainReader, header *type
|
|||
log.Error("[IsAuthorisedAddress] Can't get snapshot with at ", "number", header.Number, "hash", header.Hash().Hex(), "err", err)
|
||||
return false
|
||||
}
|
||||
for _, mn := range snap.NextEpochMasterNodes {
|
||||
for _, mn := range snap.NextEpochCandidates {
|
||||
if mn == address {
|
||||
return true
|
||||
}
|
||||
|
|
@ -515,6 +516,15 @@ func (x *XDPoS_v2) UpdateMasternodes(chain consensus.ChainReader, header *types.
|
|||
return nil
|
||||
}
|
||||
|
||||
// VerifyUncles implements consensus.Engine, always returning an error for any
|
||||
// uncles as this consensus mechanism doesn't permit uncles.
|
||||
func (x *XDPoS_v2) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
|
||||
if len(block.Uncles()) > 0 {
|
||||
return errors.New("uncles not allowed in XDPoS_v2")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool) error {
|
||||
err := x.verifyHeader(chain, header, nil, fullVerify)
|
||||
if err != nil {
|
||||
|
|
@ -611,9 +621,9 @@ func (x *XDPoS_v2) VerifyVoteMessage(chain consensus.ChainReader, vote *types.Vo
|
|||
verified, signer, err := x.verifyMsgSignature(types.VoteSigHash(&types.VoteForSign{
|
||||
ProposedBlockInfo: vote.ProposedBlockInfo,
|
||||
GapNumber: vote.GapNumber,
|
||||
}), vote.Signature, snapshot.NextEpochMasterNodes)
|
||||
}), vote.Signature, snapshot.NextEpochCandidates)
|
||||
if err != nil {
|
||||
for i, mn := range snapshot.NextEpochMasterNodes {
|
||||
for i, mn := range snapshot.NextEpochCandidates {
|
||||
log.Warn("[VerifyVoteMessage] Master node list item", "index", i, "Master node", mn.Hex())
|
||||
}
|
||||
log.Warn("[VerifyVoteMessage] Error while verifying vote message", "votedBlockNum", vote.ProposedBlockInfo.Number.Uint64(), "votedBlockHash", vote.ProposedBlockInfo.Hash.Hex(), "voteHash", vote.Hash(), "error", err.Error())
|
||||
|
|
@ -649,15 +659,15 @@ func (x *XDPoS_v2) VerifyTimeoutMessage(chain consensus.ChainReader, timeoutMsg
|
|||
log.Error("[VerifyTimeoutMessage] Fail to get snapshot when verifying timeout message!", "messageGapNumber", timeoutMsg.GapNumber, "err", err)
|
||||
return false, err
|
||||
}
|
||||
if len(snap.NextEpochMasterNodes) == 0 {
|
||||
log.Error("[VerifyTimeoutMessage] cannot find nextEpochMasterNodes from snapshot", "messageGapNumber", timeoutMsg.GapNumber)
|
||||
if len(snap.NextEpochCandidates) == 0 {
|
||||
log.Error("[VerifyTimeoutMessage] cannot find NextEpochCandidates from snapshot", "messageGapNumber", timeoutMsg.GapNumber)
|
||||
return false, fmt.Errorf("Empty master node lists from snapshot")
|
||||
}
|
||||
|
||||
verified, signer, err := x.verifyMsgSignature(types.TimeoutSigHash(&types.TimeoutForSign{
|
||||
Round: timeoutMsg.Round,
|
||||
GapNumber: timeoutMsg.GapNumber,
|
||||
}), timeoutMsg.Signature, snap.NextEpochMasterNodes)
|
||||
}), timeoutMsg.Signature, snap.NextEpochCandidates)
|
||||
|
||||
if err != nil {
|
||||
log.Warn("[VerifyTimeoutMessage] cannot verify timeout signature", "err", err)
|
||||
|
|
@ -1005,7 +1015,7 @@ func (x *XDPoS_v2) calcMasternodes(chain consensus.ChainReader, blockNum *big.In
|
|||
log.Error("[calcMasternodes] Adaptor v2 getSnapshot has error", "err", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
candidates := snap.NextEpochMasterNodes
|
||||
candidates := snap.NextEpochCandidates
|
||||
|
||||
if blockNum.Uint64() == x.config.V2.SwitchBlock.Uint64()+1 {
|
||||
log.Info("[calcMasternodes] examing first v2 block")
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types
|
|||
return nil, err
|
||||
}
|
||||
penalties := common.ExtractAddressFromBytes(h.Penalties)
|
||||
candidates := snap.NextEpochMasterNodes
|
||||
candidates := snap.NextEpochCandidates
|
||||
standbynodes := []common.Address{}
|
||||
if len(masternodes) != len(candidates) {
|
||||
standbynodes = candidates
|
||||
|
|
|
|||
|
|
@ -10,22 +10,22 @@ import (
|
|||
)
|
||||
|
||||
// Snapshot is the state of the smart contract validator list
|
||||
// The validator list is used on next epoch master nodes
|
||||
// The validator list is used on next epoch candidates nodes
|
||||
// If we don't have the snapshot, then we have to trace back the gap block smart contract state which is very costly
|
||||
type SnapshotV2 struct {
|
||||
Number uint64 `json:"number"` // Block number where the snapshot was created
|
||||
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
|
||||
|
||||
// MasterNodes will get assigned on updateM1
|
||||
NextEpochMasterNodes []common.Address `json:"masterNodes"` // Set of authorized master nodes at this moment for next epoch
|
||||
// candidates will get assigned on updateM1
|
||||
NextEpochCandidates []common.Address `json:"masterNodes"` // Set of authorized candidates nodes at this moment for next epoch
|
||||
}
|
||||
|
||||
// create new snapshot for next epoch to use
|
||||
func newSnapshot(number uint64, hash common.Hash, masternodes []common.Address) *SnapshotV2 {
|
||||
func newSnapshot(number uint64, hash common.Hash, candidates []common.Address) *SnapshotV2 {
|
||||
snap := &SnapshotV2{
|
||||
Number: number,
|
||||
Hash: hash,
|
||||
NextEpochMasterNodes: masternodes,
|
||||
Number: number,
|
||||
Hash: hash,
|
||||
NextEpochCandidates: candidates,
|
||||
}
|
||||
return snap
|
||||
}
|
||||
|
|
@ -53,17 +53,17 @@ func storeSnapshot(s *SnapshotV2, db ethdb.Database) error {
|
|||
return db.Put(append([]byte("XDPoS-V2-"), s.Hash[:]...), blob)
|
||||
}
|
||||
|
||||
// retrieves master nodes list in map type
|
||||
func (s *SnapshotV2) GetMappedMasterNodes() map[common.Address]struct{} {
|
||||
// retrieves candidates nodes list in map type
|
||||
func (s *SnapshotV2) GetMappedCandidates() map[common.Address]struct{} {
|
||||
ms := make(map[common.Address]struct{})
|
||||
for _, n := range s.NextEpochMasterNodes {
|
||||
for _, n := range s.NextEpochCandidates {
|
||||
ms[n] = struct{}{}
|
||||
}
|
||||
return ms
|
||||
}
|
||||
|
||||
func (s *SnapshotV2) IsMasterNodes(address common.Address) bool {
|
||||
for _, n := range s.NextEpochMasterNodes {
|
||||
func (s *SnapshotV2) IsCandidates(address common.Address) bool {
|
||||
for _, n := range s.NextEpochCandidates {
|
||||
if n.String() == address.String() {
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ func TestGetMasterNodes(t *testing.T) {
|
|||
snap := newSnapshot(1, common.Hash{}, masterNodes)
|
||||
|
||||
for _, address := range masterNodes {
|
||||
if _, ok := snap.GetMappedMasterNodes()[address]; !ok {
|
||||
t.Error("should get master node from map", address.Hex(), snap.GetMappedMasterNodes())
|
||||
if _, ok := snap.GetMappedCandidates()[address]; !ok {
|
||||
t.Error("should get master node from map", address.Hex(), snap.GetMappedCandidates())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time
|
|||
log.Error("[verifyTC] Fail to get snapshot when verifying TC!", "TCGapNumber", timeoutCert.GapNumber)
|
||||
return fmt.Errorf("[verifyTC] Unable to get snapshot, %s", err)
|
||||
}
|
||||
if snap == nil || len(snap.NextEpochMasterNodes) == 0 {
|
||||
if snap == nil || len(snap.NextEpochCandidates) == 0 {
|
||||
log.Error("[verifyTC] Something wrong with the snapshot from gapNumber", "messageGapNumber", timeoutCert.GapNumber, "snapshot", snap)
|
||||
return fmt.Errorf("empty master node lists from snapshot")
|
||||
}
|
||||
|
|
@ -135,7 +135,7 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time
|
|||
for _, signature := range signatures {
|
||||
go func(sig types.Signature) {
|
||||
defer wg.Done()
|
||||
verified, _, err := x.verifyMsgSignature(signedTimeoutObj, sig, snap.NextEpochMasterNodes)
|
||||
verified, _, err := x.verifyMsgSignature(signedTimeoutObj, sig, snap.NextEpochCandidates)
|
||||
if err != nil || !verified {
|
||||
log.Error("[verifyTC] Error or verification failure", "Signature", sig, "Error", err)
|
||||
mutex.Lock() // Lock before accessing haveError
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ func (x *XDPoS_v2) GetSignersFromSnapshot(chain consensus.ChainReader, header *t
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return snap.NextEpochMasterNodes, err
|
||||
return snap.NextEpochCandidates, err
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) CalculateMissingRounds(chain consensus.ChainReader, header *types.Header) (*utils.PublicApiMissedRoundsMetadata, error) {
|
||||
|
|
|
|||
|
|
@ -122,8 +122,20 @@ func (x *XDPoS_v2) verifyVotes(chain consensus.ChainReader, votes map[common.Has
|
|||
for h, vote := range votes {
|
||||
go func(hash common.Hash, v *types.Vote) {
|
||||
defer wg.Done()
|
||||
if v.GetSigner() != emptySigner {
|
||||
// verify before
|
||||
signerAddress := v.GetSigner()
|
||||
if signerAddress != emptySigner {
|
||||
// verify that signer belongs to the final masternodes, we have not do so in previous steps
|
||||
if len(masternodes) == 0 {
|
||||
log.Error("[verifyVotes] empty masternode list detected when verifying message signatures")
|
||||
}
|
||||
for _, mn := range masternodes {
|
||||
if mn == signerAddress {
|
||||
return
|
||||
}
|
||||
}
|
||||
// if signer does not belong to final masternodes, we remove the signer
|
||||
v.SetSigner(emptySigner)
|
||||
log.Debug("[verifyVotes] find a vote does not belong to final masternodes", "signer", signerAddress)
|
||||
return
|
||||
}
|
||||
signedVote := types.VoteSigHash(&types.VoteForSign{
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
|
||||
"github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate"
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/prque"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/clique"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
|
||||
)
|
||||
|
||||
type Masternode struct {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ package clique
|
|||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"sync"
|
||||
|
|
@ -316,6 +317,16 @@ func (c *Clique) verifyHeader(chain consensus.ChainReader, header *types.Header,
|
|||
return errInvalidDifficulty
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that the gas limit is <= 2^63-1
|
||||
if header.GasLimit > params.MaxGasLimit {
|
||||
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, params.MaxGasLimit)
|
||||
}
|
||||
|
||||
// Verify that the gasUsed is <= gasLimit
|
||||
if header.GasUsed > header.GasLimit {
|
||||
return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit)
|
||||
}
|
||||
// If all checks passed, validate any special fields for hard forks
|
||||
if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil {
|
||||
return err
|
||||
|
|
@ -347,6 +358,11 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *type
|
|||
if parent.Time.Uint64()+c.config.Period > header.Time.Uint64() {
|
||||
return ErrInvalidTimestamp
|
||||
}
|
||||
// Verify that the gas limit remains within allowed bounds
|
||||
if err := misc.VerifyGaslimit(parent.GasLimit, header.GasLimit); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Retrieve the snapshot needed to verify this header and cache it
|
||||
snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
|
||||
if err != nil {
|
||||
|
|
|
|||
41
consensus/misc/gaslimit.go
Normal file
41
consensus/misc/gaslimit.go
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2021 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 misc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
)
|
||||
|
||||
// VerifyGaslimit verifies the header gas limit according increase/decrease
|
||||
// in relation to the parent gas limit.
|
||||
func VerifyGaslimit(parentGasLimit, headerGasLimit uint64) error {
|
||||
// Verify that the gas limit remains within allowed bounds
|
||||
diff := int64(parentGasLimit) - int64(headerGasLimit)
|
||||
if diff < 0 {
|
||||
diff *= -1
|
||||
}
|
||||
limit := parentGasLimit / params.GasLimitBoundDivisor
|
||||
if uint64(diff) >= limit {
|
||||
return fmt.Errorf("invalid gas limit: have %d, want %d +-= %d", headerGasLimit, parentGasLimit, limit-1)
|
||||
}
|
||||
if headerGasLimit < params.MinGasLimit {
|
||||
return fmt.Errorf("invalid gas limit below %d", params.MinGasLimit)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -163,7 +163,7 @@ func transferTx(t *testing.T, to common.Address, transferAmount int64) *types.Tr
|
|||
amount := big.NewInt(transferAmount)
|
||||
nonce := uint64(1)
|
||||
tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
|
||||
signedTX, err := types.SignTx(tx, types.NewEIP155Signer(big.NewInt(chainID)), voterKey)
|
||||
signedTX, err := types.SignTx(tx, types.LatestSignerForChainID(big.NewInt(chainID)), voterKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -183,7 +183,7 @@ func voteTX(gasLimit uint64, nonce uint64, addr string) (*types.Transaction, err
|
|||
to := common.HexToAddress(common.MasternodeVotingSMC)
|
||||
tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
|
||||
|
||||
signedTX, err := types.SignTx(tx, types.NewEIP155Signer(big.NewInt(chainID)), voterKey)
|
||||
signedTX, err := types.SignTx(tx, types.LatestSignerForChainID(big.NewInt(chainID)), voterKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -411,7 +411,7 @@ func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*ty
|
|||
// nonce := uint64(0)
|
||||
// to := common.HexToAddress("xdc35658f7b2a9e7701e65e7a654659eb1c481d1dc5")
|
||||
// tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
|
||||
// signedTX, err := types.SignTx(tx, types.NewEIP155Signer(big.NewInt(chainID)), acc4Key)
|
||||
// signedTX, err := types.SignTx(tx, types.LatestSignerForChainID(big.NewInt(chainID)), acc4Key)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ func voteTX(gasLimit uint64, nonce uint64, addr string) (*types.Transaction, err
|
|||
to := common.HexToAddress(common.MasternodeVotingSMC)
|
||||
tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
|
||||
|
||||
signedTX, err := types.SignTx(tx, types.NewEIP155Signer(big.NewInt(chainID)), voterKey)
|
||||
signedTX, err := types.SignTx(tx, types.LatestSignerForChainID(big.NewInt(chainID)), voterKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -285,7 +285,7 @@ func getMultiCandidatesBackend(t *testing.T, chainConfig *params.ChainConfig, n
|
|||
|
||||
func signingTxWithKey(header *types.Header, nonce uint64, privateKey *ecdsa.PrivateKey) (*types.Transaction, error) {
|
||||
tx := contracts.CreateTxSign(header.Number, header.Hash(), nonce, common.HexToAddress(common.BlockSigners))
|
||||
s := types.NewEIP155Signer(big.NewInt(chainID))
|
||||
s := types.LatestSignerForChainID(big.NewInt(chainID))
|
||||
h := s.Hash(tx)
|
||||
sig, err := crypto.Sign(h[:], privateKey)
|
||||
if err != nil {
|
||||
|
|
@ -300,7 +300,7 @@ func signingTxWithKey(header *types.Header, nonce uint64, privateKey *ecdsa.Priv
|
|||
|
||||
func signingTxWithSignerFn(header *types.Header, nonce uint64, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) (*types.Transaction, error) {
|
||||
tx := contracts.CreateTxSign(header.Number, header.Hash(), nonce, common.HexToAddress(common.BlockSigners))
|
||||
s := types.NewEIP155Signer(big.NewInt(chainID))
|
||||
s := types.LatestSignerForChainID(big.NewInt(chainID))
|
||||
h := s.Hash(tx)
|
||||
sig, err := signFn(accounts.Account{Address: signer}, h[:])
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ func TestYourTurnInitialV2(t *testing.T) {
|
|||
assert.NotNil(t, snap)
|
||||
masterNodes := adaptor.EngineV1.GetMasternodesFromCheckpointHeader(block900.Header())
|
||||
for i := 0; i < len(masterNodes); i++ {
|
||||
assert.Equal(t, masterNodes[i].Hex(), snap.NextEpochMasterNodes[i].Hex())
|
||||
assert.Equal(t, masterNodes[i].Hex(), snap.NextEpochCandidates[i].Hex())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,7 +136,7 @@ func TestUpdateMasterNodes(t *testing.T) {
|
|||
snap, err = x.GetSnapshot(blockchain, parentBlock.Header())
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, snap.IsMasterNodes(voterAddr))
|
||||
assert.True(t, snap.IsCandidates(voterAddr))
|
||||
assert.Equal(t, int(snap.Number), 1350)
|
||||
}
|
||||
|
||||
|
|
@ -211,7 +211,7 @@ func TestPrepareHappyPath(t *testing.T) {
|
|||
}
|
||||
|
||||
validators := []byte{}
|
||||
for _, v := range snap.NextEpochMasterNodes {
|
||||
for _, v := range snap.NextEpochCandidates {
|
||||
validators = append(validators, v[:]...)
|
||||
}
|
||||
assert.Equal(t, validators, header901.Validators)
|
||||
|
|
@ -267,7 +267,7 @@ func TestUpdateMultipleMasterNodes(t *testing.T) {
|
|||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1350, int(snap.Number))
|
||||
assert.Equal(t, 128, len(snap.NextEpochMasterNodes)) // 128 is all masternode candidates, not limited by MaxMasternodes
|
||||
assert.Equal(t, 128, len(snap.NextEpochCandidates)) // 128 is all masternode candidates, not limited by MaxMasternodes
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -286,10 +286,10 @@ func TestConfigSwitchOnDifferentMasternodeCount(t *testing.T) {
|
|||
|
||||
snap, err := x.GetSnapshot(blockchain, currentBlock.Header())
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(snap.NextEpochMasterNodes), 20)
|
||||
assert.Equal(t, len(snap.NextEpochCandidates), 20)
|
||||
header1800.Validators = []byte{}
|
||||
for i := 0; i < 20; i++ {
|
||||
header1800.Validators = append(header1800.Validators, snap.NextEpochMasterNodes[i].Bytes()...)
|
||||
header1800.Validators = append(header1800.Validators, snap.NextEpochCandidates[i].Bytes()...)
|
||||
}
|
||||
|
||||
round, err := x.GetRoundNumber(header1800)
|
||||
|
|
|
|||
|
|
@ -91,10 +91,13 @@ func TestFeeTxWithTRC21Token(t *testing.T) {
|
|||
t.Fatal("check balance after fail transfer in tr20: ", err, "get", balance, "transfer", airDropAmount)
|
||||
}
|
||||
|
||||
//check balance fee
|
||||
// check balance fee
|
||||
balanceIssuerFee, err = trc21Issuer.GetTokenCapacity(trc21TokenAddr)
|
||||
if err != nil || balanceIssuerFee.Cmp(remainFee) != 0 {
|
||||
t.Fatal("can't get balance token fee in smart contract: ", err, "got", balanceIssuerFee, "wanted", remainFee)
|
||||
if err != nil {
|
||||
t.Fatal("can't get balance token fee in smart contract: ", err)
|
||||
}
|
||||
if balanceIssuerFee.Cmp(remainFee) != 0 {
|
||||
t.Fatal("check balance token fee in smart contract: got", balanceIssuerFee, "wanted", remainFee)
|
||||
}
|
||||
//check trc21 SMC balance
|
||||
balance, err = contractBackend.BalanceAt(nil, trc21IssuerAddr, nil)
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ func CreateTransactionSign(chainConfig *params.ChainConfig, pool *core.TxPool, m
|
|||
}
|
||||
|
||||
// Create and send tx to smart contract for sign validate block.
|
||||
nonce := pool.State().GetNonce(account.Address)
|
||||
nonce := pool.Nonce(account.Address)
|
||||
tx := CreateTxSign(block.Number(), block.Hash(), nonce, common.HexToAddress(common.BlockSigners))
|
||||
txSigned, err := wallet.SignTx(account, tx, chainConfig.ChainId)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
|||
return func(i int, gen *BlockGen) {
|
||||
toaddr := common.Address{}
|
||||
data := make([]byte, nbytes)
|
||||
gas, _ := IntrinsicGas(data, false, false)
|
||||
gas, _ := IntrinsicGas(data, nil, false, false)
|
||||
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey)
|
||||
gen.AddTx(tx)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,13 +28,12 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
|
||||
"github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate"
|
||||
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/mclock"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/prque"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/sort"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||
|
|
@ -53,13 +52,17 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
"github.com/XinFinOrg/XDPoSChain/trie"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
|
||||
)
|
||||
|
||||
var (
|
||||
blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil)
|
||||
CheckpointCh = make(chan int)
|
||||
ErrNoGenesis = errors.New("Genesis not found in chain")
|
||||
|
||||
blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil)
|
||||
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
|
||||
blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
|
||||
blockReorgInvalidatedTx = metrics.NewRegisteredMeter("chain/reorg/invalidTx", nil)
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -201,7 +204,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
|||
chainConfig: chainConfig,
|
||||
cacheConfig: cacheConfig,
|
||||
db: db,
|
||||
triegc: prque.New(),
|
||||
triegc: prque.New(nil),
|
||||
stateCache: state.NewDatabase(db),
|
||||
quit: make(chan struct{}),
|
||||
bodyCache: bodyCache,
|
||||
|
|
@ -254,6 +257,11 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
|||
return bc, nil
|
||||
}
|
||||
|
||||
// GetVMConfig returns the block chain VM config.
|
||||
func (bc *BlockChain) GetVMConfig() *vm.Config {
|
||||
return &bc.vmConfig
|
||||
}
|
||||
|
||||
// NewBlockChainEx extend old blockchain, add order state db
|
||||
func NewBlockChainEx(db ethdb.Database, XDCxDb ethdb.XDCxDatabase, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config) (*BlockChain, error) {
|
||||
blockchain, err := NewBlockChain(db, cacheConfig, chainConfig, engine, vmConfig)
|
||||
|
|
@ -607,7 +615,7 @@ func (bc *BlockChain) repair(head **types.Block) error {
|
|||
if ok {
|
||||
tradingService := engine.GetXDCXService()
|
||||
lendingService := engine.GetLendingService()
|
||||
if bc.Config().IsTIPXDCX((*head).Number()) && bc.chainConfig.XDPoS != nil && (*head).NumberU64() > bc.chainConfig.XDPoS.Epoch && tradingService != nil && lendingService != nil {
|
||||
if bc.Config().IsTIPXDCXReceiver((*head).Number()) && bc.chainConfig.XDPoS != nil && (*head).NumberU64() > bc.chainConfig.XDPoS.Epoch && tradingService != nil && lendingService != nil {
|
||||
author, _ := bc.Engine().Author((*head).Header())
|
||||
tradingRoot, err := tradingService.GetTradingStateRoot(*head, author)
|
||||
if err == nil {
|
||||
|
|
@ -913,7 +921,7 @@ func (bc *BlockChain) SaveData() {
|
|||
if err := triedb.Commit(recent.Root(), true); err != nil {
|
||||
log.Error("Failed to commit recent state trie", "err", err)
|
||||
}
|
||||
if bc.Config().IsTIPXDCX(recent.Number()) && bc.chainConfig.XDPoS != nil && recent.NumberU64() > bc.chainConfig.XDPoS.Epoch && engine != nil {
|
||||
if bc.Config().IsTIPXDCXReceiver(recent.Number()) && bc.chainConfig.XDPoS != nil && recent.NumberU64() > bc.chainConfig.XDPoS.Epoch && engine != nil {
|
||||
author, _ := bc.Engine().Author(recent.Header())
|
||||
if tradingService != nil {
|
||||
tradingRoot, _ := tradingService.GetTradingStateRoot(recent, author)
|
||||
|
|
@ -1049,6 +1057,11 @@ func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts ty
|
|||
// The transaction hash can be retrieved from the transaction itself
|
||||
receipts[j].TxHash = transactions[j].Hash()
|
||||
|
||||
// block location fields
|
||||
receipts[j].BlockHash = block.Hash()
|
||||
receipts[j].BlockNumber = block.Number()
|
||||
receipts[j].TransactionIndex = uint(j)
|
||||
|
||||
// The contract address can be derived from the transaction itself
|
||||
if transactions[j].To() == nil {
|
||||
// Deriving the signer is expensive, only do if it's actually needed
|
||||
|
|
@ -1234,7 +1247,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
|
|||
var tradingService utils.TradingService
|
||||
var lendingTrieDb *trie.Database
|
||||
var lendingService utils.LendingService
|
||||
if bc.Config().IsTIPXDCX(block.Number()) && bc.chainConfig.XDPoS != nil && block.NumberU64() > bc.chainConfig.XDPoS.Epoch && engine != nil {
|
||||
if bc.Config().IsTIPXDCXReceiver(block.Number()) && bc.chainConfig.XDPoS != nil && block.NumberU64() > bc.chainConfig.XDPoS.Epoch && engine != nil {
|
||||
tradingService = engine.GetXDCXService()
|
||||
if tradingService != nil {
|
||||
tradingTrieDb = tradingService.GetStateCache().TrieDB()
|
||||
|
|
@ -1263,18 +1276,18 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
|
|||
} else {
|
||||
// Full but not archive node, do proper garbage collection
|
||||
triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
|
||||
bc.triegc.Push(root, -float32(block.NumberU64()))
|
||||
bc.triegc.Push(root, -int64(block.NumberU64()))
|
||||
if tradingTrieDb != nil {
|
||||
tradingTrieDb.Reference(tradingRoot, common.Hash{})
|
||||
}
|
||||
if tradingService != nil {
|
||||
tradingService.GetTriegc().Push(tradingRoot, -float32(block.NumberU64()))
|
||||
tradingService.GetTriegc().Push(tradingRoot, -int64(block.NumberU64()))
|
||||
}
|
||||
if lendingTrieDb != nil {
|
||||
lendingTrieDb.Reference(lendingRoot, common.Hash{})
|
||||
}
|
||||
if lendingService != nil {
|
||||
lendingService.GetTriegc().Push(lendingRoot, -float32(block.NumberU64()))
|
||||
lendingService.GetTriegc().Push(lendingRoot, -int64(block.NumberU64()))
|
||||
}
|
||||
if current := block.NumberU64(); current > triesInMemory {
|
||||
// Find the next state trie we need to commit
|
||||
|
|
@ -1436,7 +1449,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
|
|||
//
|
||||
// After insertion is done, all accumulated events will be fired.
|
||||
func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
n, events, logs, err := bc.insertChain(chain)
|
||||
n, events, logs, err := bc.insertChain(chain, true)
|
||||
bc.PostChainEvents(events, logs)
|
||||
return n, err
|
||||
}
|
||||
|
|
@ -1444,7 +1457,11 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
// insertChain will execute the actual chain insertion and event aggregation. The
|
||||
// only reason this method exists as a separate one is to make locking cleaner
|
||||
// with deferred statements.
|
||||
func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*types.Log, error) {
|
||||
func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []interface{}, []*types.Log, error) {
|
||||
// Sanity check that we have something meaningful to import
|
||||
if len(chain) == 0 {
|
||||
return 0, nil, nil, nil
|
||||
}
|
||||
engine, _ := bc.Engine().(*XDPoS.XDPoS)
|
||||
|
||||
// Do a sanity check that the provided chain is actually ordered and linked
|
||||
|
|
@ -1480,12 +1497,15 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
|
|||
|
||||
for i, block := range chain {
|
||||
headers[i] = block.Header()
|
||||
seals[i] = false
|
||||
seals[i] = verifySeals
|
||||
bc.downloadingBlock.Add(block.Hash(), true)
|
||||
}
|
||||
abort, results := bc.engine.VerifyHeaders(bc, headers, seals)
|
||||
defer close(abort)
|
||||
|
||||
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
|
||||
senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)
|
||||
|
||||
// Iterate over the blocks and insert when the verifier permits
|
||||
for i, block := range chain {
|
||||
// If the chain is terminating, stop processing blocks
|
||||
|
|
@ -1556,7 +1576,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
|
|||
log.Debug("Number block need calculated again", "number", block.NumberU64(), "hash", block.Hash().Hex(), "winners", len(winner))
|
||||
// Import all the pruned blocks to make the state available
|
||||
bc.chainmu.Unlock()
|
||||
_, evs, logs, err := bc.insertChain(winner)
|
||||
// During reorg, we use verifySeals=false
|
||||
_, evs, logs, err := bc.insertChain(winner, false)
|
||||
bc.chainmu.Lock()
|
||||
events, coalescedLogs = evs, logs
|
||||
|
||||
|
|
@ -1586,7 +1607,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
|
|||
var tradingService utils.TradingService
|
||||
var lendingService utils.LendingService
|
||||
isSDKNode := false
|
||||
if bc.Config().IsTIPXDCX(block.Number()) && bc.chainConfig.XDPoS != nil && engine != nil && block.NumberU64() > bc.chainConfig.XDPoS.Epoch {
|
||||
if bc.Config().IsTIPXDCXReceiver(block.Number()) && bc.chainConfig.XDPoS != nil && engine != nil && block.NumberU64() > bc.chainConfig.XDPoS.Epoch {
|
||||
author, err := bc.Engine().Author(block.Header()) // Ignore error, we're past header validation
|
||||
if err != nil {
|
||||
bc.reportBlock(block, nil, err)
|
||||
|
|
@ -1843,7 +1864,8 @@ func (bc *BlockChain) getResultBlock(block *types.Block, verifiedM2 bool) (*Resu
|
|||
}
|
||||
log.Debug("Number block need calculated again", "number", block.NumberU64(), "hash", block.Hash().Hex(), "winners", len(winner))
|
||||
// Import all the pruned blocks to make the state available
|
||||
_, _, _, err := bc.insertChain(winner)
|
||||
// During reorg, we use verifySeals=false
|
||||
_, _, _, err := bc.insertChain(winner, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1926,8 +1948,7 @@ func (bc *BlockChain) getResultBlock(block *types.Block, verifiedM2 bool) (*Resu
|
|||
}
|
||||
// liquidate / finalize open lendingTrades
|
||||
if block.Number().Uint64()%bc.chainConfig.XDPoS.Epoch == common.LiquidateLendingTradeBlock {
|
||||
finalizedTrades := map[common.Hash]*lendingstate.LendingTrade{}
|
||||
finalizedTrades, _, _, _, _, err = lendingService.ProcessLiquidationData(block.Header(), bc, statedb, tradingState, lendingState)
|
||||
finalizedTrades, _, _, _, _, err := lendingService.ProcessLiquidationData(block.Header(), bc, statedb, tradingState, lendingState)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to ProcessLiquidationData. Err: %v ", err)
|
||||
}
|
||||
|
|
@ -2048,7 +2069,7 @@ func (bc *BlockChain) insertBlock(block *types.Block) ([]interface{}, []*types.L
|
|||
// Only count canonical blocks for GC processing time
|
||||
bc.gcproc += result.proctime
|
||||
bc.UpdateBlocksHashCache(block)
|
||||
if bc.chainConfig.IsTIPXDCX(block.Number()) && bc.chainConfig.XDPoS != nil && block.NumberU64() > bc.chainConfig.XDPoS.Epoch {
|
||||
if bc.chainConfig.IsTIPXDCXReceiver(block.Number()) && bc.chainConfig.XDPoS != nil && block.NumberU64() > bc.chainConfig.XDPoS.Epoch {
|
||||
bc.logExchangeData(block)
|
||||
bc.logLendingData(block)
|
||||
}
|
||||
|
|
@ -2234,6 +2255,9 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
|||
}
|
||||
logFn("Chain split detected", "number", commonBlock.Number(), "hash", commonBlock.Hash(),
|
||||
"drop", len(oldChain), "dropfrom", oldChain[0].Hash(), "add", len(newChain), "addfrom", newChain[0].Hash())
|
||||
blockReorgAddMeter.Mark(int64(len(newChain)))
|
||||
blockReorgDropMeter.Mark(int64(len(oldChain)))
|
||||
blockReorgMeter.Mark(1)
|
||||
} else {
|
||||
log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "newnum", newBlock.Number(), "newhash", newBlock.Hash())
|
||||
}
|
||||
|
|
@ -2272,7 +2296,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
|||
}
|
||||
}()
|
||||
}
|
||||
if bc.chainConfig.IsTIPXDCX(commonBlock.Number()) && bc.chainConfig.XDPoS != nil && commonBlock.NumberU64() > bc.chainConfig.XDPoS.Epoch {
|
||||
if bc.chainConfig.IsTIPXDCXReceiver(commonBlock.Number()) && bc.chainConfig.XDPoS != nil && commonBlock.NumberU64() > bc.chainConfig.XDPoS.Epoch {
|
||||
bc.reorgTxMatches(deletedTxs, newChain)
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -561,7 +561,7 @@ func TestFastVsFullChains(t *testing.T) {
|
|||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
||||
}
|
||||
genesis = gspec.MustCommit(gendb)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, 1024, func(i int, block *BlockGen) {
|
||||
block.SetCoinbase(common.Address{0x00})
|
||||
|
|
@ -736,7 +736,7 @@ func TestChainTxReorgs(t *testing.T) {
|
|||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
|
||||
// Create two transactions shared between the chains:
|
||||
|
|
@ -842,7 +842,7 @@ func TestLogReorgs(t *testing.T) {
|
|||
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
||||
genesis = gspec.MustCommit(db)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
|
|
@ -889,7 +889,7 @@ func TestLogReorgs(t *testing.T) {
|
|||
// Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}},
|
||||
// }
|
||||
// genesis = gspec.MustCommit(db)
|
||||
// signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||
// signer = types.LatestSigner(gspec.Config)
|
||||
// )
|
||||
//
|
||||
// blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
|
|
@ -1015,7 +1015,7 @@ func TestEIP155Transition(t *testing.T) {
|
|||
funds = big.NewInt(1000000000)
|
||||
deleteAddr = common.Address{1}
|
||||
gspec = &Genesis{
|
||||
Config: ¶ms.ChainConfig{ChainId: big.NewInt(1), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)},
|
||||
Config: ¶ms.ChainConfig{ChainId: big.NewInt(1), EIP150Block: big.NewInt(0), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)},
|
||||
Alloc: GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: new(big.Int)}},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
|
|
@ -1046,7 +1046,7 @@ func TestEIP155Transition(t *testing.T) {
|
|||
}
|
||||
block.AddTx(tx)
|
||||
|
||||
tx, err = basicTx(types.NewEIP155Signer(gspec.Config.ChainId))
|
||||
tx, err = basicTx(types.LatestSigner(gspec.Config))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -1058,7 +1058,7 @@ func TestEIP155Transition(t *testing.T) {
|
|||
}
|
||||
block.AddTx(tx)
|
||||
|
||||
tx, err = basicTx(types.NewEIP155Signer(gspec.Config.ChainId))
|
||||
tx, err = basicTx(types.LatestSigner(gspec.Config))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -1086,7 +1086,7 @@ func TestEIP155Transition(t *testing.T) {
|
|||
}
|
||||
|
||||
// generate an invalid chain id transaction
|
||||
config := ¶ms.ChainConfig{ChainId: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
|
||||
config := ¶ms.ChainConfig{ChainId: big.NewInt(2), EIP150Block: big.NewInt(0), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
|
||||
blocks, _ = GenerateChain(config, blocks[len(blocks)-1], ethash.NewFaker(), db, 4, func(i int, block *BlockGen) {
|
||||
var (
|
||||
tx *types.Transaction
|
||||
|
|
@ -1095,9 +1095,8 @@ func TestEIP155Transition(t *testing.T) {
|
|||
return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil), signer, key)
|
||||
}
|
||||
)
|
||||
switch i {
|
||||
case 0:
|
||||
tx, err = basicTx(types.NewEIP155Signer(big.NewInt(2)))
|
||||
if i == 0 {
|
||||
tx, err = basicTx(types.LatestSigner(config))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -1136,7 +1135,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
|||
var (
|
||||
tx *types.Transaction
|
||||
err error
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
switch i {
|
||||
case 0:
|
||||
|
|
@ -1167,7 +1166,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
|||
t.Error("account should not exist")
|
||||
}
|
||||
|
||||
// account musn't be created post eip 161
|
||||
// account mustn't be created post eip 161
|
||||
if _, err := blockchain.InsertChain(types.Blocks{blocks[2]}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -1412,3 +1411,93 @@ func TestAreTwoBlocksSamePath(t *testing.T) {
|
|||
})
|
||||
|
||||
}
|
||||
|
||||
// TestEIP2718Transition tests that an EIP-2718 transaction will be accepted
|
||||
// after the fork block has passed. This is verified by sending an EIP-2930
|
||||
// access list transaction, which specifies a single slot access, and then
|
||||
// checking that the gas usage of a hot SLOAD and a cold SLOAD are calculated
|
||||
// correctly.
|
||||
func TestEIP2718Transition(t *testing.T) {
|
||||
var (
|
||||
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
||||
|
||||
// Generate a canonical chain to act as the main dataset
|
||||
engine = ethash.NewFaker()
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
|
||||
// A sender who makes transactions, has some funds
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
gspec = &Genesis{
|
||||
Config: ¶ms.ChainConfig{
|
||||
ChainId: new(big.Int).SetBytes([]byte("eip1559")),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
DAOForkBlock: nil,
|
||||
DAOForkSupport: true,
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: big.NewInt(0),
|
||||
PetersburgBlock: big.NewInt(0),
|
||||
IstanbulBlock: big.NewInt(0),
|
||||
Eip1559Block: big.NewInt(0),
|
||||
},
|
||||
Alloc: GenesisAlloc{
|
||||
address: {Balance: funds},
|
||||
// The address 0xAAAA sloads 0x00 and 0x01
|
||||
aa: {
|
||||
Code: []byte{
|
||||
byte(vm.PC),
|
||||
byte(vm.PC),
|
||||
byte(vm.SLOAD),
|
||||
byte(vm.SLOAD),
|
||||
},
|
||||
Nonce: 0,
|
||||
Balance: big.NewInt(0),
|
||||
},
|
||||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
)
|
||||
|
||||
blocks, _ := GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, b *BlockGen) {
|
||||
b.SetCoinbase(common.Address{1})
|
||||
|
||||
// One transaction to 0xAAAA
|
||||
signer := types.LatestSigner(gspec.Config)
|
||||
tx, _ := types.SignNewTx(key, signer, &types.AccessListTx{
|
||||
ChainID: gspec.Config.ChainId,
|
||||
Nonce: 0,
|
||||
To: &aa,
|
||||
Gas: 30000,
|
||||
GasPrice: big.NewInt(1),
|
||||
AccessList: types.AccessList{{
|
||||
Address: aa,
|
||||
StorageKeys: []common.Hash{{0}},
|
||||
}},
|
||||
})
|
||||
b.AddTx(tx)
|
||||
})
|
||||
|
||||
// Import the canonical chain
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
if n, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||
}
|
||||
|
||||
block := chain.GetBlockByNumber(1)
|
||||
|
||||
// Expected gas is intrinsic + 2 * pc + hot load + cold load, since only one load is in the access list
|
||||
expected := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas + vm.GasQuickStep*2 + vm.WarmStorageReadCostEIP2929 + vm.ColdSloadCostEIP2929
|
||||
if block.GasUsed() != expected {
|
||||
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,10 +259,9 @@ func GetBlockReceipts(db DatabaseReader, hash common.Hash, number uint64) types.
|
|||
receipts := make(types.Receipts, len(storageReceipts))
|
||||
for i, receipt := range storageReceipts {
|
||||
receipts[i] = (*types.Receipt)(receipt)
|
||||
for _, log := range receipts[i].Logs {
|
||||
// update BlockHash to fix #208
|
||||
log.BlockHash = hash
|
||||
}
|
||||
receipts[i].BlockHash = hash
|
||||
receipts[i].BlockNumber = big.NewInt(0).SetUint64(number)
|
||||
receipts[i].TransactionIndex = uint(i)
|
||||
}
|
||||
return receipts
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ package core
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto/sha3"
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
|
|
@ -335,6 +335,10 @@ func TestLookupStorage(t *testing.T) {
|
|||
func TestBlockReceiptStorage(t *testing.T) {
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
|
||||
// Create a live block since we need metadata to reconstruct the receipt
|
||||
tx1 := types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)
|
||||
tx2 := types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil)
|
||||
|
||||
receipt1 := &types.Receipt{
|
||||
Status: types.ReceiptStatusFailed,
|
||||
CumulativeGasUsed: 1,
|
||||
|
|
@ -342,10 +346,12 @@ func TestBlockReceiptStorage(t *testing.T) {
|
|||
{Address: common.BytesToAddress([]byte{0x11})},
|
||||
{Address: common.BytesToAddress([]byte{0x01, 0x11})},
|
||||
},
|
||||
TxHash: common.BytesToHash([]byte{0x11, 0x11}),
|
||||
TxHash: tx1.Hash(),
|
||||
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
|
||||
GasUsed: 111111,
|
||||
}
|
||||
receipt1.Bloom = types.CreateBloom(types.Receipts{receipt1})
|
||||
|
||||
receipt2 := &types.Receipt{
|
||||
PostState: common.Hash{2}.Bytes(),
|
||||
CumulativeGasUsed: 2,
|
||||
|
|
@ -353,10 +359,12 @@ func TestBlockReceiptStorage(t *testing.T) {
|
|||
{Address: common.BytesToAddress([]byte{0x22})},
|
||||
{Address: common.BytesToAddress([]byte{0x02, 0x22})},
|
||||
},
|
||||
TxHash: common.BytesToHash([]byte{0x22, 0x22}),
|
||||
TxHash: tx2.Hash(),
|
||||
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
|
||||
GasUsed: 222222,
|
||||
}
|
||||
receipt2.Bloom = types.CreateBloom(types.Receipts{receipt2})
|
||||
|
||||
receipts := []*types.Receipt{receipt1, receipt2}
|
||||
|
||||
// Check that no receipt entries are in a pristine database
|
||||
|
|
|
|||
|
|
@ -16,7 +16,11 @@
|
|||
|
||||
package core
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrKnownBlock is returned when a block to import is already known locally.
|
||||
|
|
@ -38,4 +42,8 @@ var (
|
|||
ErrNotFoundM1 = errors.New("list M1 not found ")
|
||||
|
||||
ErrStopPreparingBlock = errors.New("stop calculating a block not verified by M2")
|
||||
|
||||
// ErrTxTypeNotSupported is returned if a transaction is not supported in the
|
||||
// current network configuration.
|
||||
ErrTxTypeNotSupported = types.ErrTxTypeNotSupported
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
)
|
||||
|
||||
// TxPreEvent is posted when a transaction enters the transaction pool.
|
||||
type TxPreEvent struct{ Tx *types.Transaction }
|
||||
// NewTxsEvent is posted when a batch of transactions enter the transaction pool.
|
||||
type NewTxsEvent struct{ Txs []*types.Transaction }
|
||||
|
||||
// OrderTxPreEvent is posted when a order transaction enters the order transaction pool.
|
||||
type OrderTxPreEvent struct{ Tx *types.OrderTransaction }
|
||||
|
|
@ -41,9 +41,6 @@ type PendingStateEvent struct{}
|
|||
// NewMinedBlockEvent is posted when a block has been imported.
|
||||
type NewMinedBlockEvent struct{ Block *types.Block }
|
||||
|
||||
// RemovedTransactionEvent is posted when a reorg happens
|
||||
type RemovedTransactionEvent struct{ Txs types.Transactions }
|
||||
|
||||
// RemovedLogsEvent is posted when a reorg happens
|
||||
type RemovedLogsEvent struct{ Logs []*types.Log }
|
||||
|
||||
|
|
|
|||
|
|
@ -24,18 +24,16 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/prque"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/event"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -273,7 +271,7 @@ func (pool *LendingPool) loop() {
|
|||
// reset retrieves the current state of the blockchain and ensures the content
|
||||
// of the transaction pool is valid with regard to the chain state.
|
||||
func (pool *LendingPool) reset(oldHead, newblock *types.Block) {
|
||||
if !pool.chainconfig.IsTIPXDCX(pool.chain.CurrentBlock().Number()) || pool.chain.Config().XDPoS == nil || pool.chain.CurrentBlock().NumberU64() <= pool.chain.Config().XDPoS.Epoch {
|
||||
if !pool.chainconfig.IsTIPXDCXReceiver(pool.chain.CurrentBlock().Number()) || pool.chain.Config().XDPoS == nil || pool.chain.CurrentBlock().NumberU64() <= pool.chain.Config().XDPoS.Epoch {
|
||||
return
|
||||
}
|
||||
// If we're reorging an old state, reinject all dropped transactions
|
||||
|
|
@ -671,7 +669,7 @@ func (pool *LendingPool) add(tx *types.LendingTransaction, local bool) (bool, er
|
|||
// If the transaction fails basic validation, discard it
|
||||
if err := pool.validateTx(tx, local); err != nil {
|
||||
log.Debug("Discarding invalid lending transaction", "hash", hash, "userAddress", tx.UserAddress, "status", tx.Status, "err", err)
|
||||
invalidTxCounter.Inc(1)
|
||||
invalidTxMeter.Mark(1)
|
||||
return false, err
|
||||
}
|
||||
from, _ := types.LendingSender(pool.signer, tx) // already validated
|
||||
|
|
@ -685,12 +683,12 @@ func (pool *LendingPool) add(tx *types.LendingTransaction, local bool) (bool, er
|
|||
if list := pool.pending[from]; list != nil && list.Overlaps(tx) {
|
||||
inserted, old := list.Add(tx)
|
||||
if !inserted {
|
||||
pendingDiscardCounter.Inc(1)
|
||||
pendingDiscardMeter.Mark(1)
|
||||
return false, ErrPendingNonceTooLow
|
||||
}
|
||||
if old != nil {
|
||||
delete(pool.all, old.Hash())
|
||||
pendingReplaceCounter.Inc(1)
|
||||
pendingReplaceMeter.Mark(1)
|
||||
}
|
||||
pool.all[tx.Hash()] = tx
|
||||
pool.journalTx(from, tx)
|
||||
|
|
@ -726,13 +724,13 @@ func (pool *LendingPool) enqueueTx(hash common.Hash, tx *types.LendingTransactio
|
|||
inserted, old := pool.queue[from].Add(tx)
|
||||
if !inserted {
|
||||
// An older transaction was better, discard this
|
||||
queuedDiscardCounter.Inc(1)
|
||||
pendingDiscardMeter.Mark(1)
|
||||
return false, ErrPendingNonceTooLow
|
||||
}
|
||||
// Discard any previous transaction and mark this
|
||||
if old != nil {
|
||||
delete(pool.all, old.Hash())
|
||||
queuedReplaceCounter.Inc(1)
|
||||
queuedReplaceMeter.Mark(1)
|
||||
}
|
||||
pool.all[hash] = tx
|
||||
return old != nil, nil
|
||||
|
|
@ -764,13 +762,13 @@ func (pool *LendingPool) promoteTx(addr common.Address, hash common.Hash, tx *ty
|
|||
if !inserted {
|
||||
// An older transaction was better, discard this
|
||||
delete(pool.all, hash)
|
||||
pendingDiscardCounter.Inc(1)
|
||||
pendingDiscardMeter.Mark(1)
|
||||
return
|
||||
}
|
||||
// Otherwise discard any previous transaction and mark this
|
||||
if old != nil {
|
||||
delete(pool.all, old.Hash())
|
||||
pendingReplaceCounter.Inc(1)
|
||||
pendingReplaceMeter.Mark(1)
|
||||
}
|
||||
// Failsafe to work around direct pending inserts (tests)
|
||||
if pool.all[hash] == nil {
|
||||
|
|
@ -814,7 +812,7 @@ func (pool *LendingPool) AddRemotes(txs []*types.LendingTransaction) []error {
|
|||
|
||||
// addTx enqueues a single transaction into the pool if it is valid.
|
||||
func (pool *LendingPool) addTx(tx *types.LendingTransaction, local bool) error {
|
||||
if !pool.chainconfig.IsTIPXDCX(pool.chain.CurrentBlock().Number()) {
|
||||
if !pool.chainconfig.IsTIPXDCXReceiver(pool.chain.CurrentBlock().Number()) {
|
||||
return nil
|
||||
}
|
||||
tx.CacheHash()
|
||||
|
|
@ -981,7 +979,7 @@ func (pool *LendingPool) promoteExecutables(accounts []common.Address) {
|
|||
hash := tx.Hash()
|
||||
delete(pool.all, hash)
|
||||
|
||||
queuedRateLimitCounter.Inc(1)
|
||||
queuedRateLimitMeter.Mark(1)
|
||||
log.Trace("Removed cap-exceeding queued transaction", "hash", hash)
|
||||
}
|
||||
}
|
||||
|
|
@ -998,11 +996,11 @@ func (pool *LendingPool) promoteExecutables(accounts []common.Address) {
|
|||
if pending > pool.config.GlobalSlots {
|
||||
pendingBeforeCap := pending
|
||||
// Assemble a spam order to penalize large transactors first
|
||||
spammers := prque.New()
|
||||
spammers := prque.New(nil)
|
||||
for addr, list := range pool.pending {
|
||||
// Only evict transactions from high rollers
|
||||
if !pool.locals.contains(addr) && uint64(list.Len()) > pool.config.AccountSlots {
|
||||
spammers.Push(addr, float32(list.Len()))
|
||||
spammers.Push(addr, int64(list.Len()))
|
||||
}
|
||||
}
|
||||
// Gradually drop transactions from offenders
|
||||
|
|
@ -1057,7 +1055,7 @@ func (pool *LendingPool) promoteExecutables(accounts []common.Address) {
|
|||
}
|
||||
}
|
||||
}
|
||||
pendingRateLimitCounter.Inc(int64(pendingBeforeCap - pending))
|
||||
pendingRateLimitMeter.Mark(int64(pendingBeforeCap - pending))
|
||||
}
|
||||
// If we've queued more transactions than the hard limit, drop oldest ones
|
||||
queued := uint64(0)
|
||||
|
|
@ -1066,7 +1064,7 @@ func (pool *LendingPool) promoteExecutables(accounts []common.Address) {
|
|||
}
|
||||
if queued > pool.config.GlobalQueue {
|
||||
// Sort all accounts with queued transactions by heartbeat
|
||||
addresses := make(addresssByHeartbeat, 0, len(pool.queue))
|
||||
addresses := make(addressesByHeartbeat, 0, len(pool.queue))
|
||||
for addr := range pool.queue {
|
||||
if !pool.locals.contains(addr) { // don't drop locals
|
||||
addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]})
|
||||
|
|
@ -1087,7 +1085,7 @@ func (pool *LendingPool) promoteExecutables(accounts []common.Address) {
|
|||
pool.removeTx(tx.Hash())
|
||||
}
|
||||
drop -= size
|
||||
queuedRateLimitCounter.Inc(int64(size))
|
||||
queuedRateLimitMeter.Mark(int64(size))
|
||||
continue
|
||||
}
|
||||
// Otherwise drop only last few transactions
|
||||
|
|
@ -1095,7 +1093,7 @@ func (pool *LendingPool) promoteExecutables(accounts []common.Address) {
|
|||
for i := len(txs) - 1; i >= 0 && drop > 0; i-- {
|
||||
pool.removeTx(txs[i].Hash())
|
||||
drop--
|
||||
queuedRateLimitCounter.Inc(1)
|
||||
queuedRateLimitMeter.Mark(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,16 +25,15 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/prque"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/event"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -278,7 +277,7 @@ func (pool *OrderPool) loop() {
|
|||
// reset retrieves the current state of the blockchain and ensures the content
|
||||
// of the transaction pool is valid with regard to the chain state.
|
||||
func (pool *OrderPool) reset(oldHead, newblock *types.Block) {
|
||||
if !pool.chainconfig.IsTIPXDCX(pool.chain.CurrentBlock().Number()) || pool.chain.Config().XDPoS == nil || pool.chain.CurrentBlock().NumberU64() <= pool.chain.Config().XDPoS.Epoch {
|
||||
if !pool.chainconfig.IsTIPXDCXReceiver(pool.chain.CurrentBlock().Number()) || pool.chain.Config().XDPoS == nil || pool.chain.CurrentBlock().NumberU64() <= pool.chain.Config().XDPoS.Epoch {
|
||||
return
|
||||
}
|
||||
// If we're reorging an old state, reinject all dropped transactions
|
||||
|
|
@ -579,7 +578,7 @@ func (pool *OrderPool) add(tx *types.OrderTransaction, local bool) (bool, error)
|
|||
// If the transaction fails basic validation, discard it
|
||||
if err := pool.validateTx(tx, local); err != nil {
|
||||
log.Debug("Discarding invalid order transaction", "hash", hash, "userAddress", tx.UserAddress().Hex(), "status", tx.Status, "err", err)
|
||||
invalidTxCounter.Inc(1)
|
||||
invalidTxMeter.Mark(1)
|
||||
return false, err
|
||||
}
|
||||
from, _ := types.OrderSender(pool.signer, tx) // already validated
|
||||
|
|
@ -593,12 +592,12 @@ func (pool *OrderPool) add(tx *types.OrderTransaction, local bool) (bool, error)
|
|||
if list := pool.pending[from]; list != nil && list.Overlaps(tx) {
|
||||
inserted, old := list.Add(tx)
|
||||
if !inserted {
|
||||
pendingDiscardCounter.Inc(1)
|
||||
pendingDiscardMeter.Mark(1)
|
||||
return false, ErrPendingNonceTooLow
|
||||
}
|
||||
if old != nil {
|
||||
delete(pool.all, old.Hash())
|
||||
pendingReplaceCounter.Inc(1)
|
||||
pendingReplaceMeter.Mark(1)
|
||||
}
|
||||
pool.all[tx.Hash()] = tx
|
||||
pool.journalTx(from, tx)
|
||||
|
|
@ -636,13 +635,13 @@ func (pool *OrderPool) enqueueTx(hash common.Hash, tx *types.OrderTransaction) (
|
|||
inserted, old := pool.queue[from].Add(tx)
|
||||
if !inserted {
|
||||
// An older transaction was better, discard this
|
||||
queuedDiscardCounter.Inc(1)
|
||||
queuedDiscardMeter.Mark(1)
|
||||
return false, ErrPendingNonceTooLow
|
||||
}
|
||||
// Discard any previous transaction and mark this
|
||||
if old != nil {
|
||||
delete(pool.all, old.Hash())
|
||||
queuedReplaceCounter.Inc(1)
|
||||
queuedReplaceMeter.Mark(1)
|
||||
}
|
||||
pool.all[hash] = tx
|
||||
return old != nil, nil
|
||||
|
|
@ -675,13 +674,13 @@ func (pool *OrderPool) promoteTx(addr common.Address, hash common.Hash, tx *type
|
|||
if !inserted {
|
||||
// An older transaction was better, discard this
|
||||
delete(pool.all, hash)
|
||||
pendingDiscardCounter.Inc(1)
|
||||
pendingDiscardMeter.Mark(1)
|
||||
return
|
||||
}
|
||||
// Otherwise discard any previous transaction and mark this
|
||||
if old != nil {
|
||||
delete(pool.all, old.Hash())
|
||||
pendingReplaceCounter.Inc(1)
|
||||
pendingReplaceMeter.Mark(1)
|
||||
}
|
||||
// Failsafe to work around direct pending inserts (tests)
|
||||
if pool.all[hash] == nil {
|
||||
|
|
@ -729,7 +728,7 @@ func (pool *OrderPool) AddRemotes(txs []*types.OrderTransaction) []error {
|
|||
|
||||
// addTx enqueues a single transaction into the pool if it is valid.
|
||||
func (pool *OrderPool) addTx(tx *types.OrderTransaction, local bool) error {
|
||||
if !pool.chainconfig.IsTIPXDCX(pool.chain.CurrentBlock().Number()) {
|
||||
if !pool.chainconfig.IsTIPXDCXReceiver(pool.chain.CurrentBlock().Number()) {
|
||||
return nil
|
||||
}
|
||||
tx.CacheHash()
|
||||
|
|
@ -896,7 +895,7 @@ func (pool *OrderPool) promoteExecutables(accounts []common.Address) {
|
|||
hash := tx.Hash()
|
||||
delete(pool.all, hash)
|
||||
|
||||
queuedRateLimitCounter.Inc(1)
|
||||
queuedRateLimitMeter.Mark(1)
|
||||
log.Debug("Removed cap-exceeding queued transaction", "addr", tx.UserAddress().Hex(), "nonce", tx.Nonce(), "ohash", tx.OrderHash().Hex(), "status", tx.Status(), "orderid", tx.OrderID())
|
||||
}
|
||||
}
|
||||
|
|
@ -914,11 +913,11 @@ func (pool *OrderPool) promoteExecutables(accounts []common.Address) {
|
|||
if pending > pool.config.GlobalSlots {
|
||||
pendingBeforeCap := pending
|
||||
// Assemble a spam order to penalize large transactors first
|
||||
spammers := prque.New()
|
||||
spammers := prque.New(nil)
|
||||
for addr, list := range pool.pending {
|
||||
// Only evict transactions from high rollers
|
||||
if !pool.locals.contains(addr) && uint64(list.Len()) > pool.config.AccountSlots {
|
||||
spammers.Push(addr, float32(list.Len()))
|
||||
spammers.Push(addr, int64(list.Len()))
|
||||
}
|
||||
}
|
||||
// Gradually drop transactions from offenders
|
||||
|
|
@ -973,7 +972,7 @@ func (pool *OrderPool) promoteExecutables(accounts []common.Address) {
|
|||
}
|
||||
}
|
||||
}
|
||||
pendingRateLimitCounter.Inc(int64(pendingBeforeCap - pending))
|
||||
pendingRateLimitMeter.Mark(int64(pendingBeforeCap - pending))
|
||||
}
|
||||
// If we've queued more transactions than the hard limit, drop oldest ones
|
||||
queued := uint64(0)
|
||||
|
|
@ -982,7 +981,7 @@ func (pool *OrderPool) promoteExecutables(accounts []common.Address) {
|
|||
}
|
||||
if queued > pool.config.GlobalQueue {
|
||||
// Sort all accounts with queued transactions by heartbeat
|
||||
addresses := make(addresssByHeartbeat, 0, len(pool.queue))
|
||||
addresses := make(addressesByHeartbeat, 0, len(pool.queue))
|
||||
for addr := range pool.queue {
|
||||
if !pool.locals.contains(addr) { // don't drop locals
|
||||
addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]})
|
||||
|
|
@ -1003,7 +1002,7 @@ func (pool *OrderPool) promoteExecutables(accounts []common.Address) {
|
|||
pool.removeTx(tx.Hash())
|
||||
}
|
||||
drop -= size
|
||||
queuedRateLimitCounter.Inc(int64(size))
|
||||
queuedRateLimitMeter.Mark(int64(size))
|
||||
continue
|
||||
}
|
||||
// Otherwise drop only last few transactions
|
||||
|
|
@ -1011,7 +1010,7 @@ func (pool *OrderPool) promoteExecutables(accounts []common.Address) {
|
|||
for i := len(txs) - 1; i >= 0 && drop > 0; i-- {
|
||||
pool.removeTx(txs[i].Hash())
|
||||
drop--
|
||||
queuedRateLimitCounter.Inc(1)
|
||||
queuedRateLimitMeter.Mark(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
136
core/state/access_list.go
Normal file
136
core/state/access_list.go
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
// Copyright 2020 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 state
|
||||
|
||||
import (
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
)
|
||||
|
||||
type accessList struct {
|
||||
addresses map[common.Address]int
|
||||
slots []map[common.Hash]struct{}
|
||||
}
|
||||
|
||||
// ContainsAddress returns true if the address is in the access list.
|
||||
func (al *accessList) ContainsAddress(address common.Address) bool {
|
||||
_, ok := al.addresses[address]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Contains checks if a slot within an account is present in the access list, returning
|
||||
// separate flags for the presence of the account and the slot respectively.
|
||||
func (al *accessList) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
|
||||
idx, ok := al.addresses[address]
|
||||
if !ok {
|
||||
// no such address (and hence zero slots)
|
||||
return false, false
|
||||
}
|
||||
if idx == -1 {
|
||||
// address yes, but no slots
|
||||
return true, false
|
||||
}
|
||||
_, slotPresent = al.slots[idx][slot]
|
||||
return true, slotPresent
|
||||
}
|
||||
|
||||
// newAccessList creates a new accessList.
|
||||
func newAccessList() *accessList {
|
||||
return &accessList{
|
||||
addresses: make(map[common.Address]int),
|
||||
}
|
||||
}
|
||||
|
||||
// Copy creates an independent copy of an accessList.
|
||||
func (a *accessList) Copy() *accessList {
|
||||
cp := newAccessList()
|
||||
for k, v := range a.addresses {
|
||||
cp.addresses[k] = v
|
||||
}
|
||||
cp.slots = make([]map[common.Hash]struct{}, len(a.slots))
|
||||
for i, slotMap := range a.slots {
|
||||
newSlotmap := make(map[common.Hash]struct{}, len(slotMap))
|
||||
for k := range slotMap {
|
||||
newSlotmap[k] = struct{}{}
|
||||
}
|
||||
cp.slots[i] = newSlotmap
|
||||
}
|
||||
return cp
|
||||
}
|
||||
|
||||
// AddAddress adds an address to the access list, and returns 'true' if the operation
|
||||
// caused a change (addr was not previously in the list).
|
||||
func (al *accessList) AddAddress(address common.Address) bool {
|
||||
if _, present := al.addresses[address]; present {
|
||||
return false
|
||||
}
|
||||
al.addresses[address] = -1
|
||||
return true
|
||||
}
|
||||
|
||||
// AddSlot adds the specified (addr, slot) combo to the access list.
|
||||
// Return values are:
|
||||
// - address added
|
||||
// - slot added
|
||||
// For any 'true' value returned, a corresponding journal entry must be made.
|
||||
func (al *accessList) AddSlot(address common.Address, slot common.Hash) (addrChange bool, slotChange bool) {
|
||||
idx, addrPresent := al.addresses[address]
|
||||
if !addrPresent || idx == -1 {
|
||||
// Address not present, or addr present but no slots there
|
||||
al.addresses[address] = len(al.slots)
|
||||
slotmap := map[common.Hash]struct{}{slot: {}}
|
||||
al.slots = append(al.slots, slotmap)
|
||||
return !addrPresent, true
|
||||
}
|
||||
// There is already an (address,slot) mapping
|
||||
slotmap := al.slots[idx]
|
||||
if _, ok := slotmap[slot]; !ok {
|
||||
slotmap[slot] = struct{}{}
|
||||
// Journal add slot change
|
||||
return false, true
|
||||
}
|
||||
// No changes required
|
||||
return false, false
|
||||
}
|
||||
|
||||
// DeleteSlot removes an (address, slot)-tuple from the access list.
|
||||
// This operation needs to be performed in the same order as the addition happened.
|
||||
// This method is meant to be used by the journal, which maintains ordering of
|
||||
// operations.
|
||||
func (al *accessList) DeleteSlot(address common.Address, slot common.Hash) {
|
||||
idx, addrOk := al.addresses[address]
|
||||
// There are two ways this can fail
|
||||
if !addrOk {
|
||||
panic("reverting slot change, address not present in list")
|
||||
}
|
||||
slotmap := al.slots[idx]
|
||||
delete(slotmap, slot)
|
||||
// If that was the last (first) slot, remove it
|
||||
// Since additions and rollbacks are always performed in order,
|
||||
// we can delete the item without worrying about screwing up later indices
|
||||
if len(slotmap) == 0 {
|
||||
al.slots = al.slots[:idx]
|
||||
al.addresses[address] = -1
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteAddress removes an address from the access list. This operation
|
||||
// needs to be performed in the same order as the addition happened.
|
||||
// This method is meant to be used by the journal, which maintains ordering of
|
||||
// operations.
|
||||
func (al *accessList) DeleteAddress(address common.Address) {
|
||||
delete(al.addresses, address)
|
||||
}
|
||||
|
|
@ -28,6 +28,11 @@ type journalEntry interface {
|
|||
|
||||
type journal []journalEntry
|
||||
|
||||
// length returns the current number of entries in the journal.
|
||||
func (j *journal) length() int {
|
||||
return len(*j)
|
||||
}
|
||||
|
||||
type (
|
||||
// Changes to the account trie.
|
||||
createObjectChange struct {
|
||||
|
|
@ -75,6 +80,14 @@ type (
|
|||
prev bool
|
||||
prevDirty bool
|
||||
}
|
||||
// Changes to the access list
|
||||
accessListAddAccountChange struct {
|
||||
address *common.Address
|
||||
}
|
||||
accessListAddSlotChange struct {
|
||||
address *common.Address
|
||||
slot *common.Hash
|
||||
}
|
||||
)
|
||||
|
||||
func (ch createObjectChange) undo(s *StateDB) {
|
||||
|
|
@ -138,3 +151,20 @@ func (ch addLogChange) undo(s *StateDB) {
|
|||
func (ch addPreimageChange) undo(s *StateDB) {
|
||||
delete(s.preimages, ch.hash)
|
||||
}
|
||||
|
||||
func (ch accessListAddAccountChange) undo(s *StateDB) {
|
||||
/*
|
||||
One important invariant here, is that whenever a (addr, slot) is added, if the
|
||||
addr is not already present, the add causes two journal entries:
|
||||
- one for the address,
|
||||
- one for the (address,slot)
|
||||
Therefore, when unrolling the change, we can always blindly delete the
|
||||
(addr) at this point, since no storage adds can remain when come upon
|
||||
a single (addr) change.
|
||||
*/
|
||||
s.accessList.DeleteAddress(*ch.address)
|
||||
}
|
||||
|
||||
func (ch accessListAddSlotChange) undo(s *StateDB) {
|
||||
s.accessList.DeleteSlot(*ch.address, *ch.slot)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,143 +0,0 @@
|
|||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
)
|
||||
|
||||
type account struct {
|
||||
stateObject *stateObject
|
||||
nstart uint64
|
||||
nonces []bool
|
||||
}
|
||||
|
||||
type ManagedState struct {
|
||||
*StateDB
|
||||
|
||||
mu sync.RWMutex
|
||||
|
||||
accounts map[common.Address]*account
|
||||
}
|
||||
|
||||
// ManagedState returns a new managed state with the statedb as it's backing layer
|
||||
func ManageState(statedb *StateDB) *ManagedState {
|
||||
return &ManagedState{
|
||||
StateDB: statedb.Copy(),
|
||||
accounts: make(map[common.Address]*account),
|
||||
}
|
||||
}
|
||||
|
||||
// SetState sets the backing layer of the managed state
|
||||
func (ms *ManagedState) SetState(statedb *StateDB) {
|
||||
ms.mu.Lock()
|
||||
defer ms.mu.Unlock()
|
||||
ms.StateDB = statedb
|
||||
}
|
||||
|
||||
// RemoveNonce removed the nonce from the managed state and all future pending nonces
|
||||
func (ms *ManagedState) RemoveNonce(addr common.Address, n uint64) {
|
||||
if ms.hasAccount(addr) {
|
||||
ms.mu.Lock()
|
||||
defer ms.mu.Unlock()
|
||||
|
||||
account := ms.getAccount(addr)
|
||||
if n-account.nstart <= uint64(len(account.nonces)) {
|
||||
reslice := make([]bool, n-account.nstart)
|
||||
copy(reslice, account.nonces[:n-account.nstart])
|
||||
account.nonces = reslice
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewNonce returns the new canonical nonce for the managed account
|
||||
func (ms *ManagedState) NewNonce(addr common.Address) uint64 {
|
||||
ms.mu.Lock()
|
||||
defer ms.mu.Unlock()
|
||||
|
||||
account := ms.getAccount(addr)
|
||||
for i, nonce := range account.nonces {
|
||||
if !nonce {
|
||||
return account.nstart + uint64(i)
|
||||
}
|
||||
}
|
||||
account.nonces = append(account.nonces, true)
|
||||
|
||||
return uint64(len(account.nonces)-1) + account.nstart
|
||||
}
|
||||
|
||||
// GetNonce returns the canonical nonce for the managed or unmanaged account.
|
||||
//
|
||||
// Because GetNonce mutates the DB, we must take a write lock.
|
||||
func (ms *ManagedState) GetNonce(addr common.Address) uint64 {
|
||||
ms.mu.Lock()
|
||||
defer ms.mu.Unlock()
|
||||
|
||||
if ms.hasAccount(addr) {
|
||||
account := ms.getAccount(addr)
|
||||
return uint64(len(account.nonces)) + account.nstart
|
||||
} else {
|
||||
return ms.StateDB.GetNonce(addr)
|
||||
}
|
||||
}
|
||||
|
||||
// SetNonce sets the new canonical nonce for the managed state
|
||||
func (ms *ManagedState) SetNonce(addr common.Address, nonce uint64) {
|
||||
ms.mu.Lock()
|
||||
defer ms.mu.Unlock()
|
||||
|
||||
so := ms.GetOrNewStateObject(addr)
|
||||
so.SetNonce(nonce)
|
||||
|
||||
ms.accounts[addr] = newAccount(so)
|
||||
}
|
||||
|
||||
// HasAccount returns whether the given address is managed or not
|
||||
func (ms *ManagedState) HasAccount(addr common.Address) bool {
|
||||
ms.mu.RLock()
|
||||
defer ms.mu.RUnlock()
|
||||
return ms.hasAccount(addr)
|
||||
}
|
||||
|
||||
func (ms *ManagedState) hasAccount(addr common.Address) bool {
|
||||
_, ok := ms.accounts[addr]
|
||||
return ok
|
||||
}
|
||||
|
||||
// populate the managed state
|
||||
func (ms *ManagedState) getAccount(addr common.Address) *account {
|
||||
if account, ok := ms.accounts[addr]; !ok {
|
||||
so := ms.GetOrNewStateObject(addr)
|
||||
ms.accounts[addr] = newAccount(so)
|
||||
} else {
|
||||
// Always make sure the state account nonce isn't actually higher
|
||||
// than the tracked one.
|
||||
so := ms.StateDB.getStateObject(addr)
|
||||
if so != nil && uint64(len(account.nonces))+account.nstart < so.Nonce() {
|
||||
ms.accounts[addr] = newAccount(so)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ms.accounts[addr]
|
||||
}
|
||||
|
||||
func newAccount(so *stateObject) *account {
|
||||
return &account{so, so.Nonce(), nil}
|
||||
}
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
)
|
||||
|
||||
var addr = common.BytesToAddress([]byte("test"))
|
||||
|
||||
func create() (*ManagedState, *account) {
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
statedb, _ := New(common.Hash{}, NewDatabase(db))
|
||||
ms := ManageState(statedb)
|
||||
ms.StateDB.SetNonce(addr, 100)
|
||||
ms.accounts[addr] = newAccount(ms.StateDB.getStateObject(addr))
|
||||
return ms, ms.accounts[addr]
|
||||
}
|
||||
|
||||
func TestNewNonce(t *testing.T) {
|
||||
ms, _ := create()
|
||||
|
||||
nonce := ms.NewNonce(addr)
|
||||
if nonce != 100 {
|
||||
t.Error("expected nonce 100. got", nonce)
|
||||
}
|
||||
|
||||
nonce = ms.NewNonce(addr)
|
||||
if nonce != 101 {
|
||||
t.Error("expected nonce 101. got", nonce)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
ms, account := create()
|
||||
|
||||
nn := make([]bool, 10)
|
||||
for i := range nn {
|
||||
nn[i] = true
|
||||
}
|
||||
account.nonces = append(account.nonces, nn...)
|
||||
|
||||
i := uint64(5)
|
||||
ms.RemoveNonce(addr, account.nstart+i)
|
||||
if len(account.nonces) != 5 {
|
||||
t.Error("expected", i, "'th index to be false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReuse(t *testing.T) {
|
||||
ms, account := create()
|
||||
|
||||
nn := make([]bool, 10)
|
||||
for i := range nn {
|
||||
nn[i] = true
|
||||
}
|
||||
account.nonces = append(account.nonces, nn...)
|
||||
|
||||
i := uint64(5)
|
||||
ms.RemoveNonce(addr, account.nstart+i)
|
||||
nonce := ms.NewNonce(addr)
|
||||
if nonce != 105 {
|
||||
t.Error("expected nonce to be 105. got", nonce)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoteNonceChange(t *testing.T) {
|
||||
ms, account := create()
|
||||
nn := make([]bool, 10)
|
||||
for i := range nn {
|
||||
nn[i] = true
|
||||
}
|
||||
account.nonces = append(account.nonces, nn...)
|
||||
ms.NewNonce(addr)
|
||||
|
||||
ms.StateDB.stateObjects[addr].data.Nonce = 200
|
||||
nonce := ms.NewNonce(addr)
|
||||
if nonce != 200 {
|
||||
t.Error("expected nonce after remote update to be", 200, "got", nonce)
|
||||
}
|
||||
ms.NewNonce(addr)
|
||||
ms.NewNonce(addr)
|
||||
ms.NewNonce(addr)
|
||||
ms.StateDB.stateObjects[addr].data.Nonce = 200
|
||||
nonce = ms.NewNonce(addr)
|
||||
if nonce != 204 {
|
||||
t.Error("expected nonce after remote update to be", 204, "got", nonce)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetNonce(t *testing.T) {
|
||||
ms, _ := create()
|
||||
|
||||
var addr common.Address
|
||||
ms.SetNonce(addr, 10)
|
||||
|
||||
if ms.GetNonce(addr) != 10 {
|
||||
t.Error("Expected nonce of 10, got", ms.GetNonce(addr))
|
||||
}
|
||||
|
||||
addr[0] = 1
|
||||
ms.StateDB.SetNonce(addr, 1)
|
||||
|
||||
if ms.GetNonce(addr) != 1 {
|
||||
t.Error("Expected nonce of 1, got", ms.GetNonce(addr))
|
||||
}
|
||||
}
|
||||
|
|
@ -31,23 +31,23 @@ var emptyCodeHash = crypto.Keccak256(nil)
|
|||
|
||||
type Code []byte
|
||||
|
||||
func (self Code) String() string {
|
||||
return string(self) //strings.Join(Disassemble(self), " ")
|
||||
func (c Code) String() string {
|
||||
return string(c) //strings.Join(Disassemble(c), " ")
|
||||
}
|
||||
|
||||
type Storage map[common.Hash]common.Hash
|
||||
|
||||
func (self Storage) String() (str string) {
|
||||
for key, value := range self {
|
||||
func (s Storage) String() (str string) {
|
||||
for key, value := range s {
|
||||
str += fmt.Sprintf("%X : %X\n", key, value)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self Storage) Copy() Storage {
|
||||
func (s Storage) Copy() Storage {
|
||||
cpy := make(Storage)
|
||||
for key, value := range self {
|
||||
for key, value := range s {
|
||||
cpy[key] = value
|
||||
}
|
||||
|
||||
|
|
@ -79,6 +79,7 @@ type stateObject struct {
|
|||
|
||||
cachedStorage Storage // Storage entry cache to avoid duplicate reads
|
||||
dirtyStorage Storage // Storage entries that need to be flushed to disk
|
||||
fakeStorage Storage // Fake storage which constructed by caller for debugging purpose.
|
||||
|
||||
// Cache flags.
|
||||
// When an object is marked suicided it will be delete from the trie
|
||||
|
|
@ -124,199 +125,236 @@ func newObject(db *StateDB, address common.Address, data Account, onDirty func(a
|
|||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
func (c *stateObject) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, c.data)
|
||||
func (s *stateObject) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, s.data)
|
||||
}
|
||||
|
||||
// setError remembers the first non-nil error it is called with.
|
||||
func (self *stateObject) setError(err error) {
|
||||
if self.dbErr == nil {
|
||||
self.dbErr = err
|
||||
func (s *stateObject) setError(err error) {
|
||||
if s.dbErr == nil {
|
||||
s.dbErr = err
|
||||
}
|
||||
}
|
||||
|
||||
func (self *stateObject) markSuicided() {
|
||||
self.suicided = true
|
||||
if self.onDirty != nil {
|
||||
self.onDirty(self.Address())
|
||||
self.onDirty = nil
|
||||
func (s *stateObject) markSuicided() {
|
||||
s.suicided = true
|
||||
if s.onDirty != nil {
|
||||
s.onDirty(s.Address())
|
||||
s.onDirty = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *stateObject) touch() {
|
||||
c.db.journal = append(c.db.journal, touchChange{
|
||||
account: &c.address,
|
||||
prev: c.touched,
|
||||
prevDirty: c.onDirty == nil,
|
||||
func (s *stateObject) touch() {
|
||||
s.db.journal = append(s.db.journal, touchChange{
|
||||
account: &s.address,
|
||||
prev: s.touched,
|
||||
prevDirty: s.onDirty == nil,
|
||||
})
|
||||
if c.onDirty != nil {
|
||||
c.onDirty(c.Address())
|
||||
c.onDirty = nil
|
||||
if s.onDirty != nil {
|
||||
s.onDirty(s.Address())
|
||||
s.onDirty = nil
|
||||
}
|
||||
c.touched = true
|
||||
s.touched = true
|
||||
}
|
||||
|
||||
func (c *stateObject) getTrie(db Database) Trie {
|
||||
if c.trie == nil {
|
||||
func (s *stateObject) getTrie(db Database) Trie {
|
||||
if s.trie == nil {
|
||||
var err error
|
||||
c.trie, err = db.OpenStorageTrie(c.addrHash, c.data.Root)
|
||||
s.trie, err = db.OpenStorageTrie(s.addrHash, s.data.Root)
|
||||
if err != nil {
|
||||
c.trie, _ = db.OpenStorageTrie(c.addrHash, common.Hash{})
|
||||
c.setError(fmt.Errorf("can't create storage trie: %v", err))
|
||||
s.trie, _ = db.OpenStorageTrie(s.addrHash, common.Hash{})
|
||||
s.setError(fmt.Errorf("can't create storage trie: %v", err))
|
||||
}
|
||||
}
|
||||
return c.trie
|
||||
return s.trie
|
||||
}
|
||||
|
||||
func (self *stateObject) GetCommittedState(db Database, key common.Hash) common.Hash {
|
||||
func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Hash {
|
||||
// If the fake storage is set, only lookup the state here(in the debugging mode)
|
||||
if s.fakeStorage != nil {
|
||||
return s.fakeStorage[key]
|
||||
}
|
||||
value := common.Hash{}
|
||||
// Load from DB in case it is missing.
|
||||
enc, err := self.getTrie(db).TryGet(key[:])
|
||||
enc, err := s.getTrie(db).TryGet(key[:])
|
||||
if err != nil {
|
||||
self.setError(err)
|
||||
s.setError(err)
|
||||
return common.Hash{}
|
||||
}
|
||||
if len(enc) > 0 {
|
||||
_, content, _, err := rlp.Split(enc)
|
||||
if err != nil {
|
||||
self.setError(err)
|
||||
s.setError(err)
|
||||
}
|
||||
value.SetBytes(content)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
|
||||
value, exists := self.cachedStorage[key]
|
||||
func (s *stateObject) GetState(db Database, key common.Hash) common.Hash {
|
||||
// If the fake storage is set, only lookup the state here(in the debugging mode)
|
||||
if s.fakeStorage != nil {
|
||||
return s.fakeStorage[key]
|
||||
}
|
||||
value, exists := s.cachedStorage[key]
|
||||
if exists {
|
||||
return value
|
||||
}
|
||||
// Load from DB in case it is missing.
|
||||
enc, err := self.getTrie(db).TryGet(key[:])
|
||||
enc, err := s.getTrie(db).TryGet(key[:])
|
||||
if err != nil {
|
||||
self.setError(err)
|
||||
s.setError(err)
|
||||
return common.Hash{}
|
||||
}
|
||||
if len(enc) > 0 {
|
||||
_, content, _, err := rlp.Split(enc)
|
||||
if err != nil {
|
||||
self.setError(err)
|
||||
s.setError(err)
|
||||
}
|
||||
value.SetBytes(content)
|
||||
}
|
||||
if (value != common.Hash{}) {
|
||||
self.cachedStorage[key] = value
|
||||
s.cachedStorage[key] = value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// SetState updates a value in account storage.
|
||||
func (self *stateObject) SetState(db Database, key, value common.Hash) {
|
||||
self.db.journal = append(self.db.journal, storageChange{
|
||||
account: &self.address,
|
||||
func (s *stateObject) SetState(db Database, key, value common.Hash) {
|
||||
// If the fake storage is set, put the temporary state update here.
|
||||
if s.fakeStorage != nil {
|
||||
s.fakeStorage[key] = value
|
||||
return
|
||||
}
|
||||
// If the new value is the same as old, don't set
|
||||
prev := s.GetState(db, key)
|
||||
if prev == value {
|
||||
return
|
||||
}
|
||||
// New value is different, update and journal the change
|
||||
s.db.journal = append(s.db.journal, storageChange{
|
||||
account: &s.address,
|
||||
key: key,
|
||||
prevalue: self.GetState(db, key),
|
||||
prevalue: prev,
|
||||
})
|
||||
self.setState(key, value)
|
||||
s.setState(key, value)
|
||||
}
|
||||
|
||||
func (self *stateObject) setState(key, value common.Hash) {
|
||||
self.cachedStorage[key] = value
|
||||
self.dirtyStorage[key] = value
|
||||
// SetStorage replaces the entire state storage with the given one.
|
||||
//
|
||||
// After this function is called, all original state will be ignored and state
|
||||
// lookup only happens in the fake state storage.
|
||||
//
|
||||
// Note this function should only be used for debugging purpose.
|
||||
func (s *stateObject) SetStorage(storage map[common.Hash]common.Hash) {
|
||||
// Allocate fake storage if it's nil.
|
||||
if s.fakeStorage == nil {
|
||||
s.fakeStorage = make(Storage)
|
||||
}
|
||||
for key, value := range storage {
|
||||
s.fakeStorage[key] = value
|
||||
}
|
||||
// Don't bother journal since this function should only be used for
|
||||
// debugging and the `fake` storage won't be committed to database.
|
||||
}
|
||||
|
||||
if self.onDirty != nil {
|
||||
self.onDirty(self.Address())
|
||||
self.onDirty = nil
|
||||
func (s *stateObject) setState(key, value common.Hash) {
|
||||
s.cachedStorage[key] = value
|
||||
s.dirtyStorage[key] = value
|
||||
|
||||
if s.onDirty != nil {
|
||||
s.onDirty(s.Address())
|
||||
s.onDirty = nil
|
||||
}
|
||||
}
|
||||
|
||||
// updateTrie writes cached storage modifications into the object's storage trie.
|
||||
func (self *stateObject) updateTrie(db Database) Trie {
|
||||
tr := self.getTrie(db)
|
||||
for key, value := range self.dirtyStorage {
|
||||
delete(self.dirtyStorage, key)
|
||||
func (s *stateObject) updateTrie(db Database) Trie {
|
||||
tr := s.getTrie(db)
|
||||
for key, value := range s.dirtyStorage {
|
||||
delete(s.dirtyStorage, key)
|
||||
if (value == common.Hash{}) {
|
||||
self.setError(tr.TryDelete(key[:]))
|
||||
s.setError(tr.TryDelete(key[:]))
|
||||
continue
|
||||
}
|
||||
// Encoding []byte cannot fail, ok to ignore the error.
|
||||
v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
|
||||
self.setError(tr.TryUpdate(key[:], v))
|
||||
s.setError(tr.TryUpdate(key[:], v))
|
||||
}
|
||||
return tr
|
||||
}
|
||||
|
||||
// UpdateRoot sets the trie root to the current root hash of
|
||||
func (self *stateObject) updateRoot(db Database) {
|
||||
self.updateTrie(db)
|
||||
self.data.Root = self.trie.Hash()
|
||||
func (s *stateObject) updateRoot(db Database) {
|
||||
s.updateTrie(db)
|
||||
s.data.Root = s.trie.Hash()
|
||||
}
|
||||
|
||||
// CommitTrie the storage trie of the object to dwb.
|
||||
// This updates the trie root.
|
||||
func (self *stateObject) CommitTrie(db Database) error {
|
||||
self.updateTrie(db)
|
||||
if self.dbErr != nil {
|
||||
return self.dbErr
|
||||
func (s *stateObject) CommitTrie(db Database) error {
|
||||
s.updateTrie(db)
|
||||
if s.dbErr != nil {
|
||||
return s.dbErr
|
||||
}
|
||||
root, err := self.trie.Commit(nil)
|
||||
root, err := s.trie.Commit(nil)
|
||||
if err == nil {
|
||||
self.data.Root = root
|
||||
s.data.Root = root
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// AddBalance removes amount from c's balance.
|
||||
// It is used to add funds to the destination account of a transfer.
|
||||
func (c *stateObject) AddBalance(amount *big.Int) {
|
||||
func (s *stateObject) AddBalance(amount *big.Int) {
|
||||
// EIP158: We must check emptiness for the objects such that the account
|
||||
// clearing (0,0,0 objects) can take effect.
|
||||
if amount.Sign() == 0 {
|
||||
if c.empty() {
|
||||
c.touch()
|
||||
if s.empty() {
|
||||
s.touch()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
c.SetBalance(new(big.Int).Add(c.Balance(), amount))
|
||||
s.SetBalance(new(big.Int).Add(s.Balance(), amount))
|
||||
}
|
||||
|
||||
// SubBalance removes amount from c's balance.
|
||||
// It is used to remove funds from the origin account of a transfer.
|
||||
func (c *stateObject) SubBalance(amount *big.Int) {
|
||||
func (s *stateObject) SubBalance(amount *big.Int) {
|
||||
if amount.Sign() == 0 {
|
||||
return
|
||||
}
|
||||
c.SetBalance(new(big.Int).Sub(c.Balance(), amount))
|
||||
s.SetBalance(new(big.Int).Sub(s.Balance(), amount))
|
||||
}
|
||||
|
||||
func (self *stateObject) SetBalance(amount *big.Int) {
|
||||
self.db.journal = append(self.db.journal, balanceChange{
|
||||
account: &self.address,
|
||||
prev: new(big.Int).Set(self.data.Balance),
|
||||
func (s *stateObject) SetBalance(amount *big.Int) {
|
||||
s.db.journal = append(s.db.journal, balanceChange{
|
||||
account: &s.address,
|
||||
prev: new(big.Int).Set(s.data.Balance),
|
||||
})
|
||||
self.setBalance(amount)
|
||||
s.setBalance(amount)
|
||||
}
|
||||
|
||||
func (self *stateObject) setBalance(amount *big.Int) {
|
||||
self.data.Balance = amount
|
||||
if self.onDirty != nil {
|
||||
self.onDirty(self.Address())
|
||||
self.onDirty = nil
|
||||
func (s *stateObject) setBalance(amount *big.Int) {
|
||||
s.data.Balance = amount
|
||||
if s.onDirty != nil {
|
||||
s.onDirty(s.Address())
|
||||
s.onDirty = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *stateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *stateObject {
|
||||
stateObject := newObject(db, self.address, self.data, onDirty)
|
||||
if self.trie != nil {
|
||||
stateObject.trie = db.db.CopyTrie(self.trie)
|
||||
func (s *stateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *stateObject {
|
||||
stateObject := newObject(db, s.address, s.data, onDirty)
|
||||
if s.trie != nil {
|
||||
stateObject.trie = db.db.CopyTrie(s.trie)
|
||||
}
|
||||
stateObject.code = self.code
|
||||
stateObject.dirtyStorage = self.dirtyStorage.Copy()
|
||||
stateObject.cachedStorage = self.dirtyStorage.Copy()
|
||||
stateObject.suicided = self.suicided
|
||||
stateObject.dirtyCode = self.dirtyCode
|
||||
stateObject.deleted = self.deleted
|
||||
stateObject.code = s.code
|
||||
stateObject.dirtyStorage = s.dirtyStorage.Copy()
|
||||
stateObject.cachedStorage = s.dirtyStorage.Copy()
|
||||
stateObject.suicided = s.suicided
|
||||
stateObject.dirtyCode = s.dirtyCode
|
||||
stateObject.deleted = s.deleted
|
||||
return stateObject
|
||||
}
|
||||
|
||||
|
|
@ -325,81 +363,81 @@ func (self *stateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)
|
|||
//
|
||||
|
||||
// Returns the address of the contract/account
|
||||
func (c *stateObject) Address() common.Address {
|
||||
return c.address
|
||||
func (s *stateObject) Address() common.Address {
|
||||
return s.address
|
||||
}
|
||||
|
||||
// Code returns the contract code associated with this object, if any.
|
||||
func (self *stateObject) Code(db Database) []byte {
|
||||
if self.code != nil {
|
||||
return self.code
|
||||
func (s *stateObject) Code(db Database) []byte {
|
||||
if s.code != nil {
|
||||
return s.code
|
||||
}
|
||||
if bytes.Equal(self.CodeHash(), emptyCodeHash) {
|
||||
if bytes.Equal(s.CodeHash(), emptyCodeHash) {
|
||||
return nil
|
||||
}
|
||||
code, err := db.ContractCode(self.addrHash, common.BytesToHash(self.CodeHash()))
|
||||
code, err := db.ContractCode(s.addrHash, common.BytesToHash(s.CodeHash()))
|
||||
if err != nil {
|
||||
self.setError(fmt.Errorf("can't load code hash %x: %v", self.CodeHash(), err))
|
||||
s.setError(fmt.Errorf("can't load code hash %x: %v", s.CodeHash(), err))
|
||||
}
|
||||
self.code = code
|
||||
s.code = code
|
||||
return code
|
||||
}
|
||||
|
||||
func (self *stateObject) SetCode(codeHash common.Hash, code []byte) {
|
||||
prevcode := self.Code(self.db.db)
|
||||
self.db.journal = append(self.db.journal, codeChange{
|
||||
account: &self.address,
|
||||
prevhash: self.CodeHash(),
|
||||
func (s *stateObject) SetCode(codeHash common.Hash, code []byte) {
|
||||
prevcode := s.Code(s.db.db)
|
||||
s.db.journal = append(s.db.journal, codeChange{
|
||||
account: &s.address,
|
||||
prevhash: s.CodeHash(),
|
||||
prevcode: prevcode,
|
||||
})
|
||||
self.setCode(codeHash, code)
|
||||
s.setCode(codeHash, code)
|
||||
}
|
||||
|
||||
func (self *stateObject) setCode(codeHash common.Hash, code []byte) {
|
||||
self.code = code
|
||||
self.data.CodeHash = codeHash[:]
|
||||
self.dirtyCode = true
|
||||
if self.onDirty != nil {
|
||||
self.onDirty(self.Address())
|
||||
self.onDirty = nil
|
||||
func (s *stateObject) setCode(codeHash common.Hash, code []byte) {
|
||||
s.code = code
|
||||
s.data.CodeHash = codeHash[:]
|
||||
s.dirtyCode = true
|
||||
if s.onDirty != nil {
|
||||
s.onDirty(s.Address())
|
||||
s.onDirty = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *stateObject) SetNonce(nonce uint64) {
|
||||
self.db.journal = append(self.db.journal, nonceChange{
|
||||
account: &self.address,
|
||||
prev: self.data.Nonce,
|
||||
func (s *stateObject) SetNonce(nonce uint64) {
|
||||
s.db.journal = append(s.db.journal, nonceChange{
|
||||
account: &s.address,
|
||||
prev: s.data.Nonce,
|
||||
})
|
||||
self.setNonce(nonce)
|
||||
s.setNonce(nonce)
|
||||
}
|
||||
|
||||
func (self *stateObject) setNonce(nonce uint64) {
|
||||
self.data.Nonce = nonce
|
||||
if self.onDirty != nil {
|
||||
self.onDirty(self.Address())
|
||||
self.onDirty = nil
|
||||
func (s *stateObject) setNonce(nonce uint64) {
|
||||
s.data.Nonce = nonce
|
||||
if s.onDirty != nil {
|
||||
s.onDirty(s.Address())
|
||||
s.onDirty = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *stateObject) CodeHash() []byte {
|
||||
return self.data.CodeHash
|
||||
func (s *stateObject) CodeHash() []byte {
|
||||
return s.data.CodeHash
|
||||
}
|
||||
|
||||
func (self *stateObject) Balance() *big.Int {
|
||||
return self.data.Balance
|
||||
func (s *stateObject) Balance() *big.Int {
|
||||
return s.data.Balance
|
||||
}
|
||||
|
||||
func (self *stateObject) Nonce() uint64 {
|
||||
return self.data.Nonce
|
||||
func (s *stateObject) Nonce() uint64 {
|
||||
return s.data.Nonce
|
||||
}
|
||||
|
||||
func (self *stateObject) Root() common.Hash {
|
||||
return self.data.Root
|
||||
func (s *stateObject) Root() common.Hash {
|
||||
return s.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.
|
||||
func (self *stateObject) Value() *big.Int {
|
||||
func (s *stateObject) Value() *big.Int {
|
||||
panic("Value on stateObject should never be called")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,9 @@ type StateDB struct {
|
|||
|
||||
preimages map[common.Hash][]byte
|
||||
|
||||
// Per-transaction access list
|
||||
accessList *accessList
|
||||
|
||||
// Journal of state modifications. This is the backbone of
|
||||
// Snapshot and RevertToSnapshot.
|
||||
journal journal
|
||||
|
|
@ -121,6 +124,7 @@ func New(root common.Hash, db Database) (*StateDB, error) {
|
|||
stateObjectsDirty: make(map[common.Address]struct{}),
|
||||
logs: make(map[common.Hash][]*types.Log),
|
||||
preimages: make(map[common.Hash][]byte),
|
||||
accessList: newAccessList(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -152,6 +156,7 @@ func (self *StateDB) Reset(root common.Hash) error {
|
|||
self.logSize = 0
|
||||
self.preimages = make(map[common.Hash][]byte)
|
||||
self.clearJournalAndRefund()
|
||||
self.accessList = newAccessList()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -239,6 +244,16 @@ func (self *StateDB) GetStorageRoot(addr common.Address) common.Hash {
|
|||
return common.Hash{}
|
||||
}
|
||||
|
||||
// TxIndex returns the current transaction index set by Prepare.
|
||||
func (self *StateDB) TxIndex() int {
|
||||
return self.txIndex
|
||||
}
|
||||
|
||||
// BlockHash returns the current block hash set by Prepare.
|
||||
func (self *StateDB) BlockHash() common.Hash {
|
||||
return self.bhash
|
||||
}
|
||||
|
||||
func (self *StateDB) GetCode(addr common.Address) []byte {
|
||||
stateObject := self.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
|
|
@ -372,6 +387,15 @@ func (self *StateDB) SetState(addr common.Address, key, value common.Hash) {
|
|||
}
|
||||
}
|
||||
|
||||
// SetStorage replaces the entire storage for the specified account with given
|
||||
// storage. This function should only be used for debugging.
|
||||
func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common.Hash) {
|
||||
stateObject := s.GetOrNewStateObject(addr)
|
||||
if stateObject != nil {
|
||||
stateObject.SetStorage(storage)
|
||||
}
|
||||
}
|
||||
|
||||
// Suicide marks the given account as suicided.
|
||||
// This clears the account balance.
|
||||
//
|
||||
|
|
@ -489,8 +513,8 @@ func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObjec
|
|||
// CreateAccount is called during the EVM CREATE operation. The situation might arise that
|
||||
// a contract does the following:
|
||||
//
|
||||
// 1. sends funds to sha(account ++ (nonce + 1))
|
||||
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
|
||||
// 1. sends funds to sha(account ++ (nonce + 1))
|
||||
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
|
||||
//
|
||||
// Carrying over the balance ensures that Ether doesn't disappear.
|
||||
func (self *StateDB) CreateAccount(addr common.Address) {
|
||||
|
|
@ -544,13 +568,26 @@ func (self *StateDB) Copy() *StateDB {
|
|||
state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state, state.MarkStateObjectDirty)
|
||||
state.stateObjectsDirty[addr] = struct{}{}
|
||||
}
|
||||
|
||||
// Deep copy the logs occurred in the scope of block
|
||||
for hash, logs := range self.logs {
|
||||
state.logs[hash] = make([]*types.Log, len(logs))
|
||||
copy(state.logs[hash], logs)
|
||||
cpy := make([]*types.Log, len(logs))
|
||||
for i, l := range logs {
|
||||
cpy[i] = new(types.Log)
|
||||
*cpy[i] = *l
|
||||
}
|
||||
state.logs[hash] = cpy
|
||||
}
|
||||
|
||||
for hash, preimage := range self.preimages {
|
||||
state.preimages[hash] = preimage
|
||||
}
|
||||
// Do we need to copy the access list? In practice: No. At the start of a
|
||||
// transaction, the access list is empty. In practice, we only ever copy state
|
||||
// _between_ transactions/blocks, never in the middle of a transaction.
|
||||
// However, it doesn't cost us much to copy an empty list, so we do it anyway
|
||||
// to not blow up if we ever decide copy it in the middle of a transaction
|
||||
state.accessList = self.accessList.Copy()
|
||||
return state
|
||||
}
|
||||
|
||||
|
|
@ -618,6 +655,7 @@ func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) {
|
|||
self.thash = thash
|
||||
self.bhash = bhash
|
||||
self.txIndex = ti
|
||||
self.accessList = newAccessList()
|
||||
}
|
||||
|
||||
// DeleteSuicides flags the suicided objects for deletion so that it
|
||||
|
|
@ -692,6 +730,67 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
|
|||
return root, err
|
||||
}
|
||||
|
||||
// PrepareAccessList handles the preparatory steps for executing a state transition with
|
||||
// regards to both EIP-2929 and EIP-2930:
|
||||
//
|
||||
// - Add sender to access list (2929)
|
||||
// - Add destination to access list (2929)
|
||||
// - Add precompiles to access list (2929)
|
||||
// - Add the contents of the optional tx access list (2930)
|
||||
//
|
||||
// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number.
|
||||
func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
|
||||
s.AddAddressToAccessList(sender)
|
||||
if dst != nil {
|
||||
s.AddAddressToAccessList(*dst)
|
||||
// If it's a create-tx, the destination will be added inside evm.create
|
||||
}
|
||||
for _, addr := range precompiles {
|
||||
s.AddAddressToAccessList(addr)
|
||||
}
|
||||
for _, el := range list {
|
||||
s.AddAddressToAccessList(el.Address)
|
||||
for _, key := range el.StorageKeys {
|
||||
s.AddSlotToAccessList(el.Address, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddAddressToAccessList adds the given address to the access list
|
||||
func (s *StateDB) AddAddressToAccessList(addr common.Address) {
|
||||
if s.accessList.AddAddress(addr) {
|
||||
s.journal = append(s.journal, accessListAddAccountChange{&addr})
|
||||
}
|
||||
}
|
||||
|
||||
// AddSlotToAccessList adds the given (address, slot)-tuple to the access list
|
||||
func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) {
|
||||
addrMod, slotMod := s.accessList.AddSlot(addr, slot)
|
||||
if addrMod {
|
||||
// In practice, this should not happen, since there is no way to enter the
|
||||
// scope of 'address' without having the 'address' become already added
|
||||
// to the access list (via call-variant, create, etc).
|
||||
// Better safe than sorry, though
|
||||
s.journal = append(s.journal, accessListAddAccountChange{&addr})
|
||||
}
|
||||
if slotMod {
|
||||
s.journal = append(s.journal, accessListAddSlotChange{
|
||||
address: &addr,
|
||||
slot: &slot,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// AddressInAccessList returns true if the given address is in the access list.
|
||||
func (s *StateDB) AddressInAccessList(addr common.Address) bool {
|
||||
return s.accessList.ContainsAddress(addr)
|
||||
}
|
||||
|
||||
// SlotInAccessList returns true if the given (address, slot)-tuple is in the access list.
|
||||
func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
|
||||
return s.accessList.Contains(addr, slot)
|
||||
}
|
||||
|
||||
func (s *StateDB) GetOwner(candidate common.Address) common.Address {
|
||||
slot := slotValidatorMapping["validatorsState"]
|
||||
// validatorsState[_candidate].owner;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import (
|
|||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
|
|
@ -32,6 +31,7 @@ import (
|
|||
check "gopkg.in/check.v1"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
)
|
||||
|
||||
|
|
@ -283,6 +283,20 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
|
|||
},
|
||||
args: make([]int64, 1),
|
||||
},
|
||||
{
|
||||
name: "AddAddressToAccessList",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.AddAddressToAccessList(addr)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "AddSlotToAccessList",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.AddSlotToAccessList(addr,
|
||||
common.Hash{byte(a.args[0])})
|
||||
},
|
||||
args: make([]int64, 1),
|
||||
},
|
||||
}
|
||||
action := actions[r.Intn(len(actions))]
|
||||
var nameargs []string
|
||||
|
|
@ -427,3 +441,177 @@ func (s *StateSuite) TestTouchDelete(c *check.C) {
|
|||
c.Fatal("expected no dirty state object")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateDBAccessList(t *testing.T) {
|
||||
// Some helpers
|
||||
addr := func(a string) common.Address {
|
||||
return common.HexToAddress(a)
|
||||
}
|
||||
slot := func(a string) common.Hash {
|
||||
return common.HexToHash(a)
|
||||
}
|
||||
|
||||
memDb := rawdb.NewMemoryDatabase()
|
||||
db := NewDatabase(memDb)
|
||||
state, _ := New(common.Hash{}, db)
|
||||
state.accessList = newAccessList()
|
||||
|
||||
verifyAddrs := func(astrings ...string) {
|
||||
t.Helper()
|
||||
// convert to common.Address form
|
||||
var addresses []common.Address
|
||||
var addressMap = make(map[common.Address]struct{})
|
||||
for _, astring := range astrings {
|
||||
address := addr(astring)
|
||||
addresses = append(addresses, address)
|
||||
addressMap[address] = struct{}{}
|
||||
}
|
||||
// Check that the given addresses are in the access list
|
||||
for _, address := range addresses {
|
||||
if !state.AddressInAccessList(address) {
|
||||
t.Fatalf("expected %x to be in access list", address)
|
||||
}
|
||||
}
|
||||
// Check that only the expected addresses are present in the acesslist
|
||||
for address := range state.accessList.addresses {
|
||||
if _, exist := addressMap[address]; !exist {
|
||||
t.Fatalf("extra address %x in access list", address)
|
||||
}
|
||||
}
|
||||
}
|
||||
verifySlots := func(addrString string, slotStrings ...string) {
|
||||
if !state.AddressInAccessList(addr(addrString)) {
|
||||
t.Fatalf("scope missing address/slots %v", addrString)
|
||||
}
|
||||
var address = addr(addrString)
|
||||
// convert to common.Hash form
|
||||
var slots []common.Hash
|
||||
var slotMap = make(map[common.Hash]struct{})
|
||||
for _, slotString := range slotStrings {
|
||||
s := slot(slotString)
|
||||
slots = append(slots, s)
|
||||
slotMap[s] = struct{}{}
|
||||
}
|
||||
// Check that the expected items are in the access list
|
||||
for i, s := range slots {
|
||||
if _, slotPresent := state.SlotInAccessList(address, s); !slotPresent {
|
||||
t.Fatalf("input %d: scope missing slot %v (address %v)", i, s, addrString)
|
||||
}
|
||||
}
|
||||
// Check that no extra elements are in the access list
|
||||
index := state.accessList.addresses[address]
|
||||
if index >= 0 {
|
||||
stateSlots := state.accessList.slots[index]
|
||||
for s := range stateSlots {
|
||||
if _, slotPresent := slotMap[s]; !slotPresent {
|
||||
t.Fatalf("scope has extra slot %v (address %v)", s, addrString)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.AddAddressToAccessList(addr("aa")) // 1
|
||||
state.AddSlotToAccessList(addr("bb"), slot("01")) // 2,3
|
||||
state.AddSlotToAccessList(addr("bb"), slot("02")) // 4
|
||||
verifyAddrs("aa", "bb")
|
||||
verifySlots("bb", "01", "02")
|
||||
|
||||
// Make a copy
|
||||
stateCopy1 := state.Copy()
|
||||
if exp, got := 4, state.journal.length(); exp != got {
|
||||
t.Fatalf("journal length mismatch: have %d, want %d", got, exp)
|
||||
}
|
||||
|
||||
// same again, should cause no journal entries
|
||||
state.AddSlotToAccessList(addr("bb"), slot("01"))
|
||||
state.AddSlotToAccessList(addr("bb"), slot("02"))
|
||||
state.AddAddressToAccessList(addr("aa"))
|
||||
if exp, got := 4, state.journal.length(); exp != got {
|
||||
t.Fatalf("journal length mismatch: have %d, want %d", got, exp)
|
||||
}
|
||||
// some new ones
|
||||
state.AddSlotToAccessList(addr("bb"), slot("03")) // 5
|
||||
state.AddSlotToAccessList(addr("aa"), slot("01")) // 6
|
||||
state.AddSlotToAccessList(addr("cc"), slot("01")) // 7,8
|
||||
state.AddAddressToAccessList(addr("cc"))
|
||||
if exp, got := 8, state.journal.length(); exp != got {
|
||||
t.Fatalf("journal length mismatch: have %d, want %d", got, exp)
|
||||
}
|
||||
|
||||
verifyAddrs("aa", "bb", "cc")
|
||||
verifySlots("aa", "01")
|
||||
verifySlots("bb", "01", "02", "03")
|
||||
verifySlots("cc", "01")
|
||||
|
||||
// now start rolling back changes
|
||||
state.journal[7].undo(state)
|
||||
if _, ok := state.SlotInAccessList(addr("cc"), slot("01")); ok {
|
||||
t.Fatalf("slot present, expected missing")
|
||||
}
|
||||
verifyAddrs("aa", "bb", "cc")
|
||||
verifySlots("aa", "01")
|
||||
verifySlots("bb", "01", "02", "03")
|
||||
|
||||
state.journal[6].undo(state)
|
||||
if state.AddressInAccessList(addr("cc")) {
|
||||
t.Fatalf("addr present, expected missing")
|
||||
}
|
||||
verifyAddrs("aa", "bb")
|
||||
verifySlots("aa", "01")
|
||||
verifySlots("bb", "01", "02", "03")
|
||||
|
||||
state.journal[5].undo(state)
|
||||
if _, ok := state.SlotInAccessList(addr("aa"), slot("01")); ok {
|
||||
t.Fatalf("slot present, expected missing")
|
||||
}
|
||||
verifyAddrs("aa", "bb")
|
||||
verifySlots("bb", "01", "02", "03")
|
||||
|
||||
state.journal[4].undo(state)
|
||||
if _, ok := state.SlotInAccessList(addr("bb"), slot("03")); ok {
|
||||
t.Fatalf("slot present, expected missing")
|
||||
}
|
||||
verifyAddrs("aa", "bb")
|
||||
verifySlots("bb", "01", "02")
|
||||
|
||||
state.journal[3].undo(state)
|
||||
if _, ok := state.SlotInAccessList(addr("bb"), slot("02")); ok {
|
||||
t.Fatalf("slot present, expected missing")
|
||||
}
|
||||
verifyAddrs("aa", "bb")
|
||||
verifySlots("bb", "01")
|
||||
|
||||
state.journal[2].undo(state)
|
||||
if _, ok := state.SlotInAccessList(addr("bb"), slot("01")); ok {
|
||||
t.Fatalf("slot present, expected missing")
|
||||
}
|
||||
verifyAddrs("aa", "bb")
|
||||
|
||||
state.journal[1].undo(state)
|
||||
if state.AddressInAccessList(addr("bb")) {
|
||||
t.Fatalf("addr present, expected missing")
|
||||
}
|
||||
verifyAddrs("aa")
|
||||
|
||||
state.journal[0].undo(state)
|
||||
if state.AddressInAccessList(addr("aa")) {
|
||||
t.Fatalf("addr present, expected missing")
|
||||
}
|
||||
if got, exp := len(state.accessList.addresses), 0; got != exp {
|
||||
t.Fatalf("expected empty, got %d", got)
|
||||
}
|
||||
if got, exp := len(state.accessList.slots), 0; got != exp {
|
||||
t.Fatalf("expected empty, got %d", got)
|
||||
}
|
||||
// Check the copy
|
||||
// Make a copy
|
||||
state = stateCopy1
|
||||
verifyAddrs("aa", "bb")
|
||||
verifySlots("bb", "01", "02")
|
||||
if got, exp := len(state.accessList.addresses), 2; got != exp {
|
||||
t.Fatalf("expected empty, got %d", got)
|
||||
}
|
||||
if got, exp := len(state.accessList.slots), 1; got != exp {
|
||||
t.Fatalf("expected empty, got %d", got)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -218,17 +218,17 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
|
|||
if tx.To() != nil && tx.To().String() == common.BlockSigners && config.IsTIPSigning(header.Number) {
|
||||
return ApplySignTransaction(config, statedb, header, tx, usedGas)
|
||||
}
|
||||
if tx.To() != nil && tx.To().String() == common.TradingStateAddr && config.IsTIPXDCX(header.Number) {
|
||||
if tx.To() != nil && tx.To().String() == common.TradingStateAddr && config.IsTIPXDCXReceiver(header.Number) {
|
||||
return ApplyEmptyTransaction(config, statedb, header, tx, usedGas)
|
||||
}
|
||||
if tx.To() != nil && tx.To().String() == common.XDCXLendingAddress && config.IsTIPXDCX(header.Number) {
|
||||
if tx.To() != nil && tx.To().String() == common.XDCXLendingAddress && config.IsTIPXDCXReceiver(header.Number) {
|
||||
return ApplyEmptyTransaction(config, statedb, header, tx, usedGas)
|
||||
}
|
||||
if tx.IsTradingTransaction() && config.IsTIPXDCX(header.Number) {
|
||||
if tx.IsTradingTransaction() && config.IsTIPXDCXReceiver(header.Number) {
|
||||
return ApplyEmptyTransaction(config, statedb, header, tx, usedGas)
|
||||
}
|
||||
|
||||
if tx.IsLendingFinalizedTradeTransaction() && config.IsTIPXDCX(header.Number) {
|
||||
if tx.IsLendingFinalizedTradeTransaction() && config.IsTIPXDCXReceiver(header.Number) {
|
||||
return ApplyEmptyTransaction(config, statedb, header, tx, usedGas)
|
||||
}
|
||||
|
||||
|
|
@ -242,7 +242,7 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
|
|||
if err != nil {
|
||||
return nil, 0, err, false
|
||||
}
|
||||
// Create a new context to be used in the EVM environment
|
||||
// Create a new context to be used in the EVM environment.
|
||||
context := NewEVMContext(msg, header, bc, author)
|
||||
// Create a new environment which holds all relevant information
|
||||
// about the transaction and calling mechanisms.
|
||||
|
|
@ -408,7 +408,8 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
|
|||
if err != nil {
|
||||
return nil, 0, err, false
|
||||
}
|
||||
// Update the state with pending changes
|
||||
|
||||
// Update the state with pending changes.
|
||||
var root []byte
|
||||
if config.IsByzantium(header.Number) {
|
||||
statedb.Finalise(true)
|
||||
|
|
@ -417,18 +418,28 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
|
|||
}
|
||||
*usedGas += gas
|
||||
|
||||
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
||||
// based on the eip phase, we're passing wether the root touch-delete accounts.
|
||||
receipt := types.NewReceipt(root, failed, *usedGas)
|
||||
// Create a new receipt for the transaction, storing the intermediate root and gas used
|
||||
// by the tx.
|
||||
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas}
|
||||
if failed {
|
||||
receipt.Status = types.ReceiptStatusFailed
|
||||
} else {
|
||||
receipt.Status = types.ReceiptStatusSuccessful
|
||||
}
|
||||
receipt.TxHash = tx.Hash()
|
||||
receipt.GasUsed = gas
|
||||
// if the transaction created a contract, store the creation address in the receipt.
|
||||
|
||||
// If the transaction created a contract, store the creation address in the receipt.
|
||||
if msg.To() == nil {
|
||||
receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
|
||||
}
|
||||
// Set the receipt logs and create a bloom for filtering
|
||||
|
||||
// Set the receipt logs and create the bloom filter.
|
||||
receipt.Logs = statedb.GetLogs(tx.Hash())
|
||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||
receipt.BlockHash = statedb.BlockHash()
|
||||
receipt.BlockNumber = header.Number
|
||||
receipt.TransactionIndex = uint(statedb.TxIndex())
|
||||
if balanceFee != nil && failed {
|
||||
state.PayFeeWithTRC21TxFail(statedb, msg.From(), *tx.To())
|
||||
}
|
||||
|
|
@ -467,6 +478,9 @@ func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, he
|
|||
statedb.AddLog(log)
|
||||
receipt.Logs = statedb.GetLogs(tx.Hash())
|
||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||
receipt.BlockHash = statedb.BlockHash()
|
||||
receipt.BlockNumber = header.Number
|
||||
receipt.TransactionIndex = uint(statedb.TxIndex())
|
||||
return receipt, 0, nil, false
|
||||
}
|
||||
|
||||
|
|
@ -491,6 +505,9 @@ func ApplyEmptyTransaction(config *params.ChainConfig, statedb *state.StateDB, h
|
|||
statedb.AddLog(log)
|
||||
receipt.Logs = statedb.GetLogs(tx.Hash())
|
||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||
receipt.BlockHash = statedb.BlockHash()
|
||||
receipt.BlockNumber = header.Number
|
||||
receipt.TransactionIndex = uint(statedb.TxIndex())
|
||||
return receipt, 0, nil, false
|
||||
}
|
||||
|
||||
|
|
@ -512,7 +529,6 @@ func InitSignerInTransactions(config *params.ChainConfig, header *types.Header,
|
|||
go func(from int, to int) {
|
||||
for j := from; j < to; j++ {
|
||||
types.CacheSigner(signer, txs[j])
|
||||
txs[j].CacheHash()
|
||||
}
|
||||
wg.Done()
|
||||
}(from, to)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
|
|
@ -42,8 +43,10 @@ The state transitioning model does all all the necessary work to work out a vali
|
|||
3) Create a new state object if the recipient is \0*32
|
||||
4) Value transfer
|
||||
== If contract creation ==
|
||||
4a) Attempt to run transaction data
|
||||
4b) If valid, use result as code for the new state object
|
||||
|
||||
4a) Attempt to run transaction data
|
||||
4b) If valid, use result as code for the new state object
|
||||
|
||||
== end ==
|
||||
5) Run Script section
|
||||
6) Derive new state root
|
||||
|
|
@ -74,13 +77,14 @@ type Message interface {
|
|||
CheckNonce() bool
|
||||
Data() []byte
|
||||
BalanceTokenFee() *big.Int
|
||||
AccessList() types.AccessList
|
||||
}
|
||||
|
||||
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
||||
func IntrinsicGas(data []byte, contractCreation, homestead bool) (uint64, error) {
|
||||
func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead bool) (uint64, error) {
|
||||
// Set the starting gas for the raw transaction
|
||||
var gas uint64
|
||||
if contractCreation && homestead {
|
||||
if isContractCreation && isHomestead {
|
||||
gas = params.TxGasContractCreation
|
||||
} else {
|
||||
gas = params.TxGas
|
||||
|
|
@ -106,6 +110,10 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool) (uint64, error)
|
|||
}
|
||||
gas += z * params.TxDataZeroGas
|
||||
}
|
||||
if accessList != nil {
|
||||
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
|
||||
gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
|
|
@ -226,7 +234,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
|
|||
contractCreation := msg.To() == nil
|
||||
|
||||
// Pay intrinsic gas
|
||||
gas, err := IntrinsicGas(st.data, contractCreation, homestead)
|
||||
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead)
|
||||
if err != nil {
|
||||
return nil, 0, false, err, nil
|
||||
}
|
||||
|
|
@ -234,6 +242,10 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
|
|||
return nil, 0, false, err, nil
|
||||
}
|
||||
|
||||
if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber); rules.IsEIP1559 {
|
||||
st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
|
||||
}
|
||||
|
||||
var (
|
||||
evm = st.evm
|
||||
// vm errors do not effect consensus and are therefor
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/contracts/XDCx/contract"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
)
|
||||
|
|
@ -37,20 +38,21 @@ const (
|
|||
getDecimalFunction = "decimals"
|
||||
)
|
||||
|
||||
// callmsg implements core.Message to allow passing it as a transaction simulator.
|
||||
type callmsg struct {
|
||||
// callMsg implements core.Message to allow passing it as a transaction simulator.
|
||||
type callMsg struct {
|
||||
ethereum.CallMsg
|
||||
}
|
||||
|
||||
func (m callmsg) From() common.Address { return m.CallMsg.From }
|
||||
func (m callmsg) Nonce() uint64 { return 0 }
|
||||
func (m callmsg) CheckNonce() bool { return false }
|
||||
func (m callmsg) To() *common.Address { return m.CallMsg.To }
|
||||
func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
||||
func (m callmsg) Gas() uint64 { return m.CallMsg.Gas }
|
||||
func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
|
||||
func (m callmsg) Data() []byte { return m.CallMsg.Data }
|
||||
func (m callmsg) BalanceTokenFee() *big.Int { return m.CallMsg.BalanceTokenFee }
|
||||
func (m callMsg) From() common.Address { return m.CallMsg.From }
|
||||
func (m callMsg) Nonce() uint64 { return 0 }
|
||||
func (m callMsg) CheckNonce() bool { return false }
|
||||
func (m callMsg) To() *common.Address { return m.CallMsg.To }
|
||||
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
||||
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
|
||||
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
|
||||
func (m callMsg) Data() []byte { return m.CallMsg.Data }
|
||||
func (m callMsg) BalanceTokenFee() *big.Int { return m.CallMsg.BalanceTokenFee }
|
||||
func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList }
|
||||
|
||||
type SimulatedBackend interface {
|
||||
CallContractWithState(call ethereum.CallMsg, chain consensus.ChainContext, statedb *state.StateDB) ([]byte, error)
|
||||
|
|
@ -86,7 +88,7 @@ func RunContract(chain consensus.ChainContext, statedb *state.StateDB, contractA
|
|||
return unpackResult, nil
|
||||
}
|
||||
|
||||
//FIXME: please use copyState for this function
|
||||
// FIXME: please use copyState for this function
|
||||
// CallContractWithState executes a contract call at the given state.
|
||||
func CallContractWithState(call ethereum.CallMsg, chain consensus.ChainContext, statedb *state.StateDB) ([]byte, error) {
|
||||
// Ensure message is initialized properly.
|
||||
|
|
@ -99,7 +101,7 @@ func CallContractWithState(call ethereum.CallMsg, chain consensus.ChainContext,
|
|||
call.Value = new(big.Int)
|
||||
}
|
||||
// Execute the call.
|
||||
msg := callmsg{call}
|
||||
msg := callMsg{call}
|
||||
feeCapacity := state.GetTRC21FeeCapacityFromState(statedb)
|
||||
if msg.To() != nil {
|
||||
if value, ok := feeCapacity[*msg.To()]; ok {
|
||||
|
|
@ -124,7 +126,7 @@ func ValidateXDCXApplyTransaction(chain consensus.ChainContext, blockNumber *big
|
|||
if blockNumber == nil || blockNumber.Sign() <= 0 {
|
||||
blockNumber = chain.CurrentHeader().Number
|
||||
}
|
||||
if !chain.Config().IsTIPXDCX(blockNumber) {
|
||||
if !chain.Config().IsTIPXDCXReceiver(blockNumber) {
|
||||
return nil
|
||||
}
|
||||
contractABI, err := GetTokenAbi(contract.TRC21ABI)
|
||||
|
|
@ -146,7 +148,7 @@ func ValidateXDCZApplyTransaction(chain consensus.ChainContext, blockNumber *big
|
|||
if blockNumber == nil || blockNumber.Sign() <= 0 {
|
||||
blockNumber = chain.CurrentHeader().Number
|
||||
}
|
||||
if !chain.Config().IsTIPXDCX(blockNumber) {
|
||||
if !chain.Config().IsTIPXDCXReceiver(blockNumber) {
|
||||
return nil
|
||||
}
|
||||
contractABI, err := GetTokenAbi(contract.TRC21ABI)
|
||||
|
|
|
|||
105
core/tx_cacher.go
Normal file
105
core/tx_cacher.go
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// 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 core
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
)
|
||||
|
||||
// senderCacher is a concurrent tranaction sender recoverer anc cacher.
|
||||
var senderCacher = newTxSenderCacher(runtime.NumCPU())
|
||||
|
||||
// txSenderCacherRequest is a request for recovering transaction senders with a
|
||||
// specific signature scheme and caching it into the transactions themselves.
|
||||
//
|
||||
// The inc field defines the number of transactions to skip after each recovery,
|
||||
// which is used to feed the same underlying input array to different threads but
|
||||
// ensure they process the early transactions fast.
|
||||
type txSenderCacherRequest struct {
|
||||
signer types.Signer
|
||||
txs []*types.Transaction
|
||||
inc int
|
||||
}
|
||||
|
||||
// txSenderCacher is a helper structure to concurrently ecrecover transaction
|
||||
// senders from digital signatures on background threads.
|
||||
type txSenderCacher struct {
|
||||
threads int
|
||||
tasks chan *txSenderCacherRequest
|
||||
}
|
||||
|
||||
// newTxSenderCacher creates a new transaction sender background cacher and starts
|
||||
// as many procesing goroutines as allowed by the GOMAXPROCS on construction.
|
||||
func newTxSenderCacher(threads int) *txSenderCacher {
|
||||
cacher := &txSenderCacher{
|
||||
tasks: make(chan *txSenderCacherRequest, threads),
|
||||
threads: threads,
|
||||
}
|
||||
for i := 0; i < threads; i++ {
|
||||
go cacher.cache()
|
||||
}
|
||||
return cacher
|
||||
}
|
||||
|
||||
// cache is an infinite loop, caching transaction senders from various forms of
|
||||
// data structures.
|
||||
func (cacher *txSenderCacher) cache() {
|
||||
for task := range cacher.tasks {
|
||||
for i := 0; i < len(task.txs); i += task.inc {
|
||||
types.Sender(task.signer, task.txs[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// recover recovers the senders from a batch of transactions and caches them
|
||||
// back into the same data structures. There is no validation being done, nor
|
||||
// any reaction to invalid signatures. That is up to calling code later.
|
||||
func (cacher *txSenderCacher) recover(signer types.Signer, txs []*types.Transaction) {
|
||||
// If there's nothing to recover, abort
|
||||
if len(txs) == 0 {
|
||||
return
|
||||
}
|
||||
// Ensure we have meaningful task sizes and schedule the recoveries
|
||||
tasks := cacher.threads
|
||||
if len(txs) < tasks*4 {
|
||||
tasks = (len(txs) + 3) / 4
|
||||
}
|
||||
for i := 0; i < tasks; i++ {
|
||||
cacher.tasks <- &txSenderCacherRequest{
|
||||
signer: signer,
|
||||
txs: txs[i:],
|
||||
inc: tasks,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// recoverFromBlocks recovers the senders from a batch of blocks and caches them
|
||||
// back into the same data structures. There is no validation being done, nor
|
||||
// any reaction to invalid signatures. That is up to calling code later.
|
||||
func (cacher *txSenderCacher) recoverFromBlocks(signer types.Signer, blocks []*types.Block) {
|
||||
count := 0
|
||||
for _, block := range blocks {
|
||||
count += len(block.Transactions())
|
||||
}
|
||||
txs := make([]*types.Transaction, 0, count)
|
||||
for _, block := range blocks {
|
||||
txs = append(txs, block.Transactions()...)
|
||||
}
|
||||
cacher.recover(signer, txs)
|
||||
}
|
||||
|
|
@ -56,7 +56,7 @@ func newTxJournal(path string) *txJournal {
|
|||
|
||||
// load parses a transaction journal dump from disk, loading its contents into
|
||||
// the specified pool.
|
||||
func (journal *txJournal) load(add func(*types.Transaction) error) error {
|
||||
func (journal *txJournal) load(add func([]*types.Transaction) []error) error {
|
||||
// Skip the parsing if the journal file doens't exist at all
|
||||
if _, err := os.Stat(journal.path); os.IsNotExist(err) {
|
||||
return nil
|
||||
|
|
@ -76,7 +76,21 @@ func (journal *txJournal) load(add func(*types.Transaction) error) error {
|
|||
stream := rlp.NewStream(input, 0)
|
||||
total, dropped := 0, 0
|
||||
|
||||
var failure error
|
||||
// Create a method to load a limited batch of transactions and bump the
|
||||
// appropriate progress counters. Then use this method to load all the
|
||||
// journalled transactions in small-ish batches.
|
||||
loadBatch := func(txs types.Transactions) {
|
||||
for _, err := range add(txs) {
|
||||
if err != nil {
|
||||
log.Debug("Failed to add journaled transaction", "err", err)
|
||||
dropped++
|
||||
}
|
||||
}
|
||||
}
|
||||
var (
|
||||
failure error
|
||||
batch types.Transactions
|
||||
)
|
||||
for {
|
||||
// Parse the next transaction and terminate on error
|
||||
tx := new(types.Transaction)
|
||||
|
|
@ -84,14 +98,16 @@ func (journal *txJournal) load(add func(*types.Transaction) error) error {
|
|||
if err != io.EOF {
|
||||
failure = err
|
||||
}
|
||||
if batch.Len() > 0 {
|
||||
loadBatch(batch)
|
||||
}
|
||||
break
|
||||
}
|
||||
// Import the transaction and bump the appropriate progress counters
|
||||
// New transaction parsed, queue up for later, import if threnshold is reached
|
||||
total++
|
||||
if err = add(tx); err != nil {
|
||||
log.Debug("Failed to add journaled transaction", "err", err)
|
||||
dropped++
|
||||
continue
|
||||
if batch = append(batch, tx); batch.Len() > 1024 {
|
||||
loadBatch(batch)
|
||||
batch = batch[:0]
|
||||
}
|
||||
}
|
||||
log.Info("Loaded local transaction journal", "transactions", total, "dropped", dropped)
|
||||
|
|
|
|||
240
core/tx_list.go
240
core/tx_list.go
|
|
@ -24,7 +24,6 @@ import (
|
|||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
)
|
||||
|
||||
// nonceHeap is a heap.Interface implementation over 64bit unsigned integers for
|
||||
|
|
@ -99,7 +98,30 @@ func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
|
|||
|
||||
// Filter iterates over the list of transactions and removes all of them for which
|
||||
// the specified function evaluates to true.
|
||||
// Filter, as opposed to 'filter', re-initialises the heap after the operation is done.
|
||||
// If you want to do several consecutive filterings, it's therefore better to first
|
||||
// do a .filter(func1) followed by .Filter(func2) or reheap()
|
||||
func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions {
|
||||
removed := m.filter(filter)
|
||||
// If transactions were removed, the heap and cache are ruined
|
||||
if len(removed) > 0 {
|
||||
m.reheap()
|
||||
}
|
||||
return removed
|
||||
}
|
||||
|
||||
func (m *txSortedMap) reheap() {
|
||||
*m.index = make([]uint64, 0, len(m.items))
|
||||
for nonce := range m.items {
|
||||
*m.index = append(*m.index, nonce)
|
||||
}
|
||||
heap.Init(m.index)
|
||||
m.cache = nil
|
||||
}
|
||||
|
||||
// filter is identical to Filter, but **does not** regenerate the heap. This method
|
||||
// should only be used if followed immediately by a call to Filter or reheap()
|
||||
func (m *txSortedMap) filter(filter func(*types.Transaction) bool) types.Transactions {
|
||||
var removed types.Transactions
|
||||
|
||||
// Collect all the transactions to filter out
|
||||
|
|
@ -109,14 +131,7 @@ func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transac
|
|||
delete(m.items, nonce)
|
||||
}
|
||||
}
|
||||
// If transactions were removed, the heap and cache are ruined
|
||||
if len(removed) > 0 {
|
||||
*m.index = make([]uint64, 0, len(m.items))
|
||||
for nonce := range m.items {
|
||||
*m.index = append(*m.index, nonce)
|
||||
}
|
||||
heap.Init(m.index)
|
||||
|
||||
m.cache = nil
|
||||
}
|
||||
return removed
|
||||
|
|
@ -197,10 +212,7 @@ func (m *txSortedMap) Len() int {
|
|||
return len(m.items)
|
||||
}
|
||||
|
||||
// Flatten creates a nonce-sorted slice of transactions based on the loosely
|
||||
// sorted internal representation. The result of the sorting is cached in case
|
||||
// it's requested again before any modifications are made to the contents.
|
||||
func (m *txSortedMap) Flatten() types.Transactions {
|
||||
func (m *txSortedMap) flatten() types.Transactions {
|
||||
// If the sorting was not cached yet, create and cache it
|
||||
if m.cache == nil {
|
||||
m.cache = make(types.Transactions, 0, len(m.items))
|
||||
|
|
@ -209,12 +221,27 @@ func (m *txSortedMap) Flatten() types.Transactions {
|
|||
}
|
||||
sort.Sort(types.TxByNonce(m.cache))
|
||||
}
|
||||
return m.cache
|
||||
}
|
||||
|
||||
// Flatten creates a nonce-sorted slice of transactions based on the loosely
|
||||
// sorted internal representation. The result of the sorting is cached in case
|
||||
// it's requested again before any modifications are made to the contents.
|
||||
func (m *txSortedMap) Flatten() types.Transactions {
|
||||
// Copy the cache to prevent accidental modifications
|
||||
txs := make(types.Transactions, len(m.cache))
|
||||
copy(txs, m.cache)
|
||||
cache := m.flatten()
|
||||
txs := make(types.Transactions, len(cache))
|
||||
copy(txs, cache)
|
||||
return txs
|
||||
}
|
||||
|
||||
// LastElement returns the last element of a flattened list, thus, the
|
||||
// transaction with the highest nonce
|
||||
func (m *txSortedMap) LastElement() *types.Transaction {
|
||||
cache := m.flatten()
|
||||
return cache[len(cache)-1]
|
||||
}
|
||||
|
||||
// txList is a "list" of transactions belonging to an account, sorted by account
|
||||
// nonce. The same type can be used both for storing contiguous transactions for
|
||||
// the executable/pending queue; and for storing gapped transactions for the non-
|
||||
|
|
@ -255,11 +282,15 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
|
|||
return false, nil
|
||||
}
|
||||
if old != nil {
|
||||
threshold := new(big.Int).Div(new(big.Int).Mul(old.GasPrice(), big.NewInt(100+int64(priceBump))), big.NewInt(100))
|
||||
// threshold = oldGP * (100 + priceBump) / 100
|
||||
a := big.NewInt(100 + int64(priceBump))
|
||||
a = a.Mul(a, old.GasPrice())
|
||||
b := big.NewInt(100)
|
||||
threshold := a.Div(a, b)
|
||||
// Have to ensure that the new gas price is higher than the old gas
|
||||
// price as well as checking the percentage threshold to ensure that
|
||||
// this is accurate for low (Wei-level) gas price replacements
|
||||
if old.GasPrice().Cmp(tx.GasPrice()) >= 0 || threshold.Cmp(tx.GasPrice()) > 0 {
|
||||
if old.GasPriceCmp(tx) >= 0 || tx.GasPriceIntCmp(threshold) < 0 {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
|
@ -303,24 +334,27 @@ func (l *txList) Filter(costLimit *big.Int, gasLimit uint64, trc21Issuers map[co
|
|||
maximum := costLimit
|
||||
if tx.To() != nil {
|
||||
if feeCapacity, ok := trc21Issuers[*tx.To()]; ok {
|
||||
return new(big.Int).Add(costLimit, feeCapacity).Cmp(tx.TxCost(number)) < 0 || tx.Gas() > gasLimit
|
||||
return tx.Gas() > gasLimit || new(big.Int).Add(costLimit, feeCapacity).Cmp(tx.TxCost(number)) < 0
|
||||
}
|
||||
}
|
||||
return tx.Cost().Cmp(maximum) > 0 || tx.Gas() > gasLimit
|
||||
return tx.Gas() > gasLimit || tx.Cost().Cmp(maximum) > 0
|
||||
})
|
||||
|
||||
// If the list was strict, filter anything above the lowest nonce
|
||||
if len(removed) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var invalids types.Transactions
|
||||
|
||||
if l.strict && len(removed) > 0 {
|
||||
// If the list was strict, filter anything above the lowest nonce
|
||||
if l.strict {
|
||||
lowest := uint64(math.MaxUint64)
|
||||
for _, tx := range removed {
|
||||
if nonce := tx.Nonce(); lowest > nonce {
|
||||
lowest = nonce
|
||||
}
|
||||
}
|
||||
invalids = l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest })
|
||||
invalids = l.txs.filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest })
|
||||
}
|
||||
l.txs.reheap()
|
||||
return removed, invalids
|
||||
}
|
||||
|
||||
|
|
@ -374,13 +408,30 @@ func (l *txList) Flatten() types.Transactions {
|
|||
return l.txs.Flatten()
|
||||
}
|
||||
|
||||
// LastElement returns the last element of a flattened list, thus, the
|
||||
// transaction with the highest nonce
|
||||
func (l *txList) LastElement() *types.Transaction {
|
||||
return l.txs.LastElement()
|
||||
}
|
||||
|
||||
// priceHeap is a heap.Interface implementation over transactions for retrieving
|
||||
// price-sorted transactions to discard when the pool fills up.
|
||||
type priceHeap []*types.Transaction
|
||||
|
||||
func (h priceHeap) Len() int { return len(h) }
|
||||
func (h priceHeap) Less(i, j int) bool { return h[i].GasPrice().Cmp(h[j].GasPrice()) < 0 }
|
||||
func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||
func (h priceHeap) Len() int { return len(h) }
|
||||
func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||
|
||||
func (h priceHeap) Less(i, j int) bool {
|
||||
// Sort primarily by price, returning the cheaper one
|
||||
switch h[i].GasPriceCmp(h[j]) {
|
||||
case -1:
|
||||
return true
|
||||
case 1:
|
||||
return false
|
||||
}
|
||||
// If the prices match, stabilize via nonces (high nonce is worse)
|
||||
return h[i].Nonce() > h[j].Nonce()
|
||||
}
|
||||
|
||||
func (h *priceHeap) Push(x interface{}) {
|
||||
*h = append(*h, x.(*types.Transaction))
|
||||
|
|
@ -395,125 +446,126 @@ func (h *priceHeap) Pop() interface{} {
|
|||
}
|
||||
|
||||
// txPricedList is a price-sorted heap to allow operating on transactions pool
|
||||
// contents in a price-incrementing way.
|
||||
// contents in a price-incrementing way. It's built opon the all transactions
|
||||
// in txpool but only interested in the remote part. It means only remote transactions
|
||||
// will be considered for tracking, sorting, eviction, etc.
|
||||
type txPricedList struct {
|
||||
all *map[common.Hash]*types.Transaction // Pointer to the map of all transactions
|
||||
items *priceHeap // Heap of prices of all the stored transactions
|
||||
stales int // Number of stale price points to (re-heap trigger)
|
||||
all *txLookup // Pointer to the map of all transactions
|
||||
remotes *priceHeap // Heap of prices of all the stored **remote** transactions
|
||||
stales int // Number of stale price points to (re-heap trigger)
|
||||
}
|
||||
|
||||
// newTxPricedList creates a new price-sorted transaction heap.
|
||||
func newTxPricedList(all *map[common.Hash]*types.Transaction) *txPricedList {
|
||||
func newTxPricedList(all *txLookup) *txPricedList {
|
||||
return &txPricedList{
|
||||
all: all,
|
||||
items: new(priceHeap),
|
||||
all: all,
|
||||
remotes: new(priceHeap),
|
||||
}
|
||||
}
|
||||
|
||||
// Put inserts a new transaction into the heap.
|
||||
func (l *txPricedList) Put(tx *types.Transaction) {
|
||||
heap.Push(l.items, tx)
|
||||
func (l *txPricedList) Put(tx *types.Transaction, local bool) {
|
||||
if local {
|
||||
return
|
||||
}
|
||||
heap.Push(l.remotes, tx)
|
||||
}
|
||||
|
||||
// Removed notifies the prices transaction list that an old transaction dropped
|
||||
// from the pool. The list will just keep a counter of stale objects and update
|
||||
// the heap if a large enough ratio of transactions go stale.
|
||||
func (l *txPricedList) Removed() {
|
||||
func (l *txPricedList) Removed(count int) {
|
||||
// Bump the stale counter, but exit if still too low (< 25%)
|
||||
l.stales++
|
||||
if l.stales <= len(*l.items)/4 {
|
||||
l.stales += count
|
||||
if l.stales <= len(*l.remotes)/4 {
|
||||
return
|
||||
}
|
||||
// Seems we've reached a critical number of stale transactions, reheap
|
||||
reheap := make(priceHeap, 0, len(*l.all))
|
||||
|
||||
l.stales, l.items = 0, &reheap
|
||||
for _, tx := range *l.all {
|
||||
*l.items = append(*l.items, tx)
|
||||
}
|
||||
heap.Init(l.items)
|
||||
l.Reheap()
|
||||
}
|
||||
|
||||
// Cap finds all the transactions below the given price threshold, drops them
|
||||
// from the priced list and returs them for further removal from the entire pool.
|
||||
func (l *txPricedList) Cap(threshold *big.Int, local *accountSet) types.Transactions {
|
||||
// from the priced list and returns them for further removal from the entire pool.
|
||||
//
|
||||
// Note: only remote transactions will be considered for eviction.
|
||||
func (l *txPricedList) Cap(threshold *big.Int) types.Transactions {
|
||||
drop := make(types.Transactions, 0, 128) // Remote underpriced transactions to drop
|
||||
save := make(types.Transactions, 0, 64) // Local underpriced transactions to keep
|
||||
|
||||
for len(*l.items) > 0 {
|
||||
for len(*l.remotes) > 0 {
|
||||
// Discard stale transactions if found during cleanup
|
||||
tx := heap.Pop(l.items).(*types.Transaction)
|
||||
if _, ok := (*l.all)[tx.Hash()]; !ok {
|
||||
cheapest := (*l.remotes)[0]
|
||||
if l.all.GetRemote(cheapest.Hash()) == nil { // Removed or migrated
|
||||
heap.Pop(l.remotes)
|
||||
l.stales--
|
||||
continue
|
||||
}
|
||||
// Stop the discards if we've reached the threshold
|
||||
if tx.GasPrice().Cmp(threshold) >= 0 {
|
||||
save = append(save, tx)
|
||||
if cheapest.GasPriceIntCmp(threshold) >= 0 {
|
||||
break
|
||||
}
|
||||
// Non stale transaction found, discard unless local
|
||||
if local.containsTx(tx) {
|
||||
save = append(save, tx)
|
||||
} else {
|
||||
drop = append(drop, tx)
|
||||
}
|
||||
}
|
||||
for _, tx := range save {
|
||||
heap.Push(l.items, tx)
|
||||
heap.Pop(l.remotes)
|
||||
drop = append(drop, cheapest)
|
||||
}
|
||||
return drop
|
||||
}
|
||||
|
||||
// Underpriced checks whether a transaction is cheaper than (or as cheap as) the
|
||||
// lowest priced transaction currently being tracked.
|
||||
func (l *txPricedList) Underpriced(tx *types.Transaction, local *accountSet) bool {
|
||||
// Local transactions cannot be underpriced
|
||||
if local.containsTx(tx) {
|
||||
return false
|
||||
}
|
||||
// lowest priced (remote) transaction currently being tracked.
|
||||
func (l *txPricedList) Underpriced(tx *types.Transaction) bool {
|
||||
// Discard stale price points if found at the heap start
|
||||
for len(*l.items) > 0 {
|
||||
head := []*types.Transaction(*l.items)[0]
|
||||
if _, ok := (*l.all)[head.Hash()]; !ok {
|
||||
for len(*l.remotes) > 0 {
|
||||
head := []*types.Transaction(*l.remotes)[0]
|
||||
if l.all.GetRemote(head.Hash()) == nil { // Removed or migrated
|
||||
l.stales--
|
||||
heap.Pop(l.items)
|
||||
heap.Pop(l.remotes)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
// Check if the transaction is underpriced or not
|
||||
if len(*l.items) == 0 {
|
||||
log.Error("Pricing query for empty pool") // This cannot happen, print to catch programming errors
|
||||
return false
|
||||
if len(*l.remotes) == 0 {
|
||||
return false // There is no remote transaction at all.
|
||||
}
|
||||
cheapest := []*types.Transaction(*l.items)[0]
|
||||
return cheapest.GasPrice().Cmp(tx.GasPrice()) >= 0
|
||||
// If the remote transaction is even cheaper than the
|
||||
// cheapest one tracked locally, reject it.
|
||||
cheapest := []*types.Transaction(*l.remotes)[0]
|
||||
return cheapest.GasPriceCmp(tx) >= 0
|
||||
}
|
||||
|
||||
// Discard finds a number of most underpriced transactions, removes them from the
|
||||
// priced list and returns them for further removal from the entire pool.
|
||||
func (l *txPricedList) Discard(count int, local *accountSet) types.Transactions {
|
||||
drop := make(types.Transactions, 0, count) // Remote underpriced transactions to drop
|
||||
save := make(types.Transactions, 0, 64) // Local underpriced transactions to keep
|
||||
|
||||
for len(*l.items) > 0 && count > 0 {
|
||||
//
|
||||
// Note local transaction won't be considered for eviction.
|
||||
func (l *txPricedList) Discard(slots int, force bool) (types.Transactions, bool) {
|
||||
drop := make(types.Transactions, 0, slots) // Remote underpriced transactions to drop
|
||||
for len(*l.remotes) > 0 && slots > 0 {
|
||||
// Discard stale transactions if found during cleanup
|
||||
tx := heap.Pop(l.items).(*types.Transaction)
|
||||
if _, ok := (*l.all)[tx.Hash()]; !ok {
|
||||
tx := heap.Pop(l.remotes).(*types.Transaction)
|
||||
if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated
|
||||
l.stales--
|
||||
continue
|
||||
}
|
||||
// Non stale transaction found, discard unless local
|
||||
if local.containsTx(tx) {
|
||||
save = append(save, tx)
|
||||
} else {
|
||||
drop = append(drop, tx)
|
||||
count--
|
||||
// Non stale transaction found, discard it
|
||||
drop = append(drop, tx)
|
||||
slots -= numSlots(tx)
|
||||
}
|
||||
// If we still can't make enough room for the new transaction
|
||||
if slots > 0 && !force {
|
||||
for _, tx := range drop {
|
||||
heap.Push(l.remotes, tx)
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
for _, tx := range save {
|
||||
heap.Push(l.items, tx)
|
||||
}
|
||||
return drop
|
||||
return drop, true
|
||||
}
|
||||
|
||||
// Reheap forcibly rebuilds the heap based on the current remote transaction set.
|
||||
func (l *txPricedList) Reheap() {
|
||||
reheap := make(priceHeap, 0, l.all.RemoteCount())
|
||||
|
||||
l.stales, l.remotes = 0, &reheap
|
||||
l.all.Range(func(hash common.Hash, tx *types.Transaction, local bool) bool {
|
||||
*l.remotes = append(*l.remotes, tx)
|
||||
return true
|
||||
}, false, true) // Only iterate remotes
|
||||
heap.Init(l.remotes)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
|
|
@ -49,3 +50,21 @@ func TestStrictTxListAdd(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTxListAdd(t *testing.B) {
|
||||
// Generate a list of transactions to insert
|
||||
key, _ := crypto.GenerateKey()
|
||||
|
||||
txs := make(types.Transactions, 100000)
|
||||
for i := 0; i < len(txs); i++ {
|
||||
txs[i] = transaction(uint64(i), 0, key)
|
||||
}
|
||||
// Insert the transactions in a random order
|
||||
list := newTxList(true)
|
||||
priceLimit := big.NewInt(int64(DefaultTxPoolConfig.PriceLimit))
|
||||
t.ResetTimer()
|
||||
for _, v := range rand.Perm(len(txs)) {
|
||||
list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
|
||||
list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump, nil, nil)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
79
core/tx_noncer.go
Normal file
79
core/tx_noncer.go
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2019 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
)
|
||||
|
||||
// txNoncer is a tiny virtual state database to manage the executable nonces of
|
||||
// accounts in the pool, falling back to reading from a real state database if
|
||||
// an account is unknown.
|
||||
type txNoncer struct {
|
||||
fallback *state.StateDB
|
||||
nonces map[common.Address]uint64
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// newTxNoncer creates a new virtual state database to track the pool nonces.
|
||||
func newTxNoncer(statedb *state.StateDB) *txNoncer {
|
||||
return &txNoncer{
|
||||
fallback: statedb.Copy(),
|
||||
nonces: make(map[common.Address]uint64),
|
||||
}
|
||||
}
|
||||
|
||||
// get returns the current nonce of an account, falling back to a real state
|
||||
// database if the account is unknown.
|
||||
func (txn *txNoncer) get(addr common.Address) uint64 {
|
||||
// We use mutex for get operation is the underlying
|
||||
// state will mutate db even for read access.
|
||||
txn.lock.Lock()
|
||||
defer txn.lock.Unlock()
|
||||
|
||||
if _, ok := txn.nonces[addr]; !ok {
|
||||
txn.nonces[addr] = txn.fallback.GetNonce(addr)
|
||||
}
|
||||
return txn.nonces[addr]
|
||||
}
|
||||
|
||||
// set inserts a new virtual nonce into the virtual state database to be returned
|
||||
// whenever the pool requests it instead of reaching into the real state database.
|
||||
func (txn *txNoncer) set(addr common.Address, nonce uint64) {
|
||||
txn.lock.Lock()
|
||||
defer txn.lock.Unlock()
|
||||
|
||||
txn.nonces[addr] = nonce
|
||||
}
|
||||
|
||||
// setIfLower updates a new virtual nonce into the virtual state database if the
|
||||
// the new one is lower.
|
||||
func (txn *txNoncer) setIfLower(addr common.Address, nonce uint64) {
|
||||
txn.lock.Lock()
|
||||
defer txn.lock.Unlock()
|
||||
|
||||
if _, ok := txn.nonces[addr]; !ok {
|
||||
txn.nonces[addr] = txn.fallback.GetNonce(addr)
|
||||
}
|
||||
if txn.nonces[addr] <= nonce {
|
||||
return
|
||||
}
|
||||
txn.nonces[addr] = nonce
|
||||
}
|
||||
1497
core/tx_pool.go
1497
core/tx_pool.go
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
115
core/types/access_list_tx.go
Normal file
115
core/types/access_list_tx.go
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
// Copyright 2020 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 types
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
)
|
||||
|
||||
//go:generate gencodec -type AccessTuple -out gen_access_tuple.go
|
||||
|
||||
// AccessList is an EIP-2930 access list.
|
||||
type AccessList []AccessTuple
|
||||
|
||||
// AccessTuple is the element type of an access list.
|
||||
type AccessTuple struct {
|
||||
Address common.Address `json:"address" gencodec:"required"`
|
||||
StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"`
|
||||
}
|
||||
|
||||
// StorageKeys returns the total number of storage keys in the access list.
|
||||
func (al AccessList) StorageKeys() int {
|
||||
sum := 0
|
||||
for _, tuple := range al {
|
||||
sum += len(tuple.StorageKeys)
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
// AccessListTx is the data of EIP-2930 access list transactions.
|
||||
type AccessListTx struct {
|
||||
ChainID *big.Int // destination chain ID
|
||||
Nonce uint64 // nonce of sender account
|
||||
GasPrice *big.Int // wei per gas
|
||||
Gas uint64 // gas limit
|
||||
To *common.Address `rlp:"nil"` // nil means contract creation
|
||||
Value *big.Int // wei amount
|
||||
Data []byte // contract invocation input data
|
||||
AccessList AccessList // EIP-2930 access list
|
||||
V, R, S *big.Int // signature values
|
||||
}
|
||||
|
||||
// copy creates a deep copy of the transaction data and initializes all fields.
|
||||
func (tx *AccessListTx) copy() TxData {
|
||||
cpy := &AccessListTx{
|
||||
Nonce: tx.Nonce,
|
||||
To: tx.To, // TODO: copy pointed-to address
|
||||
Data: common.CopyBytes(tx.Data),
|
||||
Gas: tx.Gas,
|
||||
// These are copied below.
|
||||
AccessList: make(AccessList, len(tx.AccessList)),
|
||||
Value: new(big.Int),
|
||||
ChainID: new(big.Int),
|
||||
GasPrice: new(big.Int),
|
||||
V: new(big.Int),
|
||||
R: new(big.Int),
|
||||
S: new(big.Int),
|
||||
}
|
||||
copy(cpy.AccessList, tx.AccessList)
|
||||
if tx.Value != nil {
|
||||
cpy.Value.Set(tx.Value)
|
||||
}
|
||||
if tx.ChainID != nil {
|
||||
cpy.ChainID.Set(tx.ChainID)
|
||||
}
|
||||
if tx.GasPrice != nil {
|
||||
cpy.GasPrice.Set(tx.GasPrice)
|
||||
}
|
||||
if tx.V != nil {
|
||||
cpy.V.Set(tx.V)
|
||||
}
|
||||
if tx.R != nil {
|
||||
cpy.R.Set(tx.R)
|
||||
}
|
||||
if tx.S != nil {
|
||||
cpy.S.Set(tx.S)
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
// accessors for innerTx.
|
||||
|
||||
func (tx *AccessListTx) txType() byte { return AccessListTxType }
|
||||
func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID }
|
||||
func (tx *AccessListTx) protected() bool { return true }
|
||||
func (tx *AccessListTx) accessList() AccessList { return tx.AccessList }
|
||||
func (tx *AccessListTx) data() []byte { return tx.Data }
|
||||
func (tx *AccessListTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice }
|
||||
func (tx *AccessListTx) value() *big.Int { return tx.Value }
|
||||
func (tx *AccessListTx) nonce() uint64 { return tx.Nonce }
|
||||
func (tx *AccessListTx) to() *common.Address { return tx.To }
|
||||
|
||||
func (tx *AccessListTx) rawSignatureValues() (v, r, s *big.Int) {
|
||||
return tx.V, tx.R, tx.S
|
||||
}
|
||||
|
||||
func (tx *AccessListTx) setSignatureValues(chainID, v, r, s *big.Int) {
|
||||
tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s
|
||||
}
|
||||
|
|
@ -29,7 +29,6 @@ import (
|
|||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto/sha3"
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
)
|
||||
|
||||
|
|
@ -155,13 +154,6 @@ func (h *Header) Size() common.StorageSize {
|
|||
return common.StorageSize(unsafe.Sizeof(*h)) + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen()+h.Time.BitLen())/8)
|
||||
}
|
||||
|
||||
func rlpHash(x interface{}) (h common.Hash) {
|
||||
hw := sha3.NewKeccak256()
|
||||
rlp.Encode(hw, x)
|
||||
hw.Sum(h[:0])
|
||||
return h
|
||||
}
|
||||
|
||||
// Body is a simple (mutable, non-safe) data container for storing and moving
|
||||
// a block's data contents (transactions and uncles) together.
|
||||
type Body struct {
|
||||
|
|
|
|||
|
|
@ -17,13 +17,18 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"hash"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"bytes"
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
"reflect"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// from bcValidBlockTest.json, "SimpleTx"
|
||||
|
|
@ -59,3 +64,152 @@ func TestBlockEncoding(t *testing.T) {
|
|||
t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEIP2718BlockEncoding(t *testing.T) {
|
||||
blockEnc := common.FromHex("f9031cf90214a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a0e6e49996c7ec59f7a23d22b83239a60151512c65613bf84a0d7da336399ebc4aa0cafe75574d59780665a97fbfd11365c7545aa8f1abf4e5e12e8243334ef7286bb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000820200832fefd882a410845506eb0796636f6f6c65737420626c6f636b206f6e20636861696ea0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4808080f90101f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1b89e01f89b01800a8301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000001a03dbacc8d0259f2508625e97fdfc57cd85fdd16e5821bc2c10bdd1a52649e8335a0476e10695b183a87b0aa292a7f4b78ef0c3fbe62aa2c42c84e1d9c3da159ef14c0")
|
||||
var block Block
|
||||
if err := rlp.DecodeBytes(blockEnc, &block); err != nil {
|
||||
t.Fatal("decode error: ", err)
|
||||
}
|
||||
|
||||
check := func(f string, got, want interface{}) {
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s mismatch: got %v, want %v", f, got, want)
|
||||
}
|
||||
}
|
||||
check("Difficulty", block.Difficulty(), big.NewInt(131072))
|
||||
check("GasLimit", block.GasLimit(), uint64(3141592))
|
||||
check("GasUsed", block.GasUsed(), uint64(42000))
|
||||
check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1"))
|
||||
check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498"))
|
||||
check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
|
||||
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
|
||||
check("Time", block.Time().Uint64(), uint64(1426516743))
|
||||
check("Size", block.Size(), common.StorageSize(len(blockEnc)))
|
||||
|
||||
// Create legacy tx.
|
||||
to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87")
|
||||
tx1 := NewTx(&LegacyTx{
|
||||
Nonce: 0,
|
||||
To: &to,
|
||||
Value: big.NewInt(10),
|
||||
Gas: 50000,
|
||||
GasPrice: big.NewInt(10),
|
||||
})
|
||||
sig := common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100")
|
||||
tx1, _ = tx1.WithSignature(HomesteadSigner{}, sig)
|
||||
|
||||
// Create ACL tx.
|
||||
addr := common.HexToAddress("0x0000000000000000000000000000000000000001")
|
||||
tx2 := NewTx(&AccessListTx{
|
||||
ChainID: big.NewInt(1),
|
||||
Nonce: 0,
|
||||
To: &to,
|
||||
Gas: 123457,
|
||||
GasPrice: big.NewInt(10),
|
||||
AccessList: AccessList{{Address: addr, StorageKeys: []common.Hash{{0}}}},
|
||||
})
|
||||
sig2 := common.Hex2Bytes("3dbacc8d0259f2508625e97fdfc57cd85fdd16e5821bc2c10bdd1a52649e8335476e10695b183a87b0aa292a7f4b78ef0c3fbe62aa2c42c84e1d9c3da159ef1401")
|
||||
tx2, _ = tx2.WithSignature(NewEIP2930Signer(big.NewInt(1)), sig2)
|
||||
|
||||
check("len(Transactions)", len(block.Transactions()), 2)
|
||||
check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash())
|
||||
check("Transactions[1].Hash", block.Transactions()[1].Hash(), tx2.Hash())
|
||||
check("Transactions[1].Type()", block.Transactions()[1].Type(), uint8(AccessListTxType))
|
||||
|
||||
ourBlockEnc, err := rlp.EncodeToBytes(&block)
|
||||
if err != nil {
|
||||
t.Fatal("encode error: ", err)
|
||||
}
|
||||
if !bytes.Equal(ourBlockEnc, blockEnc) {
|
||||
t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUncleHash(t *testing.T) {
|
||||
uncles := make([]*Header, 0)
|
||||
h := CalcUncleHash(uncles)
|
||||
exp := common.HexToHash("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")
|
||||
if h != exp {
|
||||
t.Fatalf("empty uncle hash is wrong, got %x != %x", h, exp)
|
||||
}
|
||||
}
|
||||
|
||||
var benchBuffer = bytes.NewBuffer(make([]byte, 0, 32000))
|
||||
|
||||
func BenchmarkEncodeBlock(b *testing.B) {
|
||||
block := makeBenchBlock()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchBuffer.Reset()
|
||||
if err := rlp.Encode(benchBuffer, block); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testHasher is the helper tool for transaction/receipt list hashing.
|
||||
// The original hasher is trie, in order to get rid of import cycle,
|
||||
// use the testing hasher instead.
|
||||
type testHasher struct {
|
||||
hasher hash.Hash
|
||||
}
|
||||
|
||||
func newHasher() *testHasher {
|
||||
return &testHasher{hasher: sha3.NewLegacyKeccak256()}
|
||||
}
|
||||
|
||||
func (h *testHasher) Reset() {
|
||||
h.hasher.Reset()
|
||||
}
|
||||
|
||||
func (h *testHasher) Update(key, val []byte) {
|
||||
h.hasher.Write(key)
|
||||
h.hasher.Write(val)
|
||||
}
|
||||
|
||||
func (h *testHasher) Hash() common.Hash {
|
||||
return common.BytesToHash(h.hasher.Sum(nil))
|
||||
}
|
||||
|
||||
func makeBenchBlock() *Block {
|
||||
var (
|
||||
key, _ = crypto.GenerateKey()
|
||||
txs = make([]*Transaction, 70)
|
||||
receipts = make([]*Receipt, len(txs))
|
||||
signer = LatestSigner(params.TestChainConfig)
|
||||
uncles = make([]*Header, 3)
|
||||
)
|
||||
header := &Header{
|
||||
Difficulty: math.BigPow(11, 11),
|
||||
Number: math.BigPow(2, 9),
|
||||
GasLimit: 12345678,
|
||||
GasUsed: 1476322,
|
||||
Time: big.NewInt(9876543),
|
||||
Extra: []byte("coolest block on chain"),
|
||||
}
|
||||
for i := range txs {
|
||||
amount := math.BigPow(2, int64(i))
|
||||
price := big.NewInt(300000)
|
||||
data := make([]byte, 100)
|
||||
tx := NewTransaction(uint64(i), common.Address{}, amount, 123457, price, data)
|
||||
signedTx, err := SignTx(tx, signer, key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
txs[i] = signedTx
|
||||
receipts[i] = NewReceipt(make([]byte, 32), false, tx.Gas())
|
||||
}
|
||||
for i := range uncles {
|
||||
uncles[i] = &Header{
|
||||
Difficulty: math.BigPow(11, 11),
|
||||
Number: math.BigPow(2, 9),
|
||||
GasLimit: 12345678,
|
||||
GasUsed: 1476322,
|
||||
Time: big.NewInt(9876543),
|
||||
Extra: []byte("benchmark uncle"),
|
||||
}
|
||||
}
|
||||
return NewBlock(header, txs, uncles, receipts)
|
||||
}
|
||||
|
|
|
|||
43
core/types/gen_access_tuple.go
Normal file
43
core/types/gen_access_tuple.go
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (a AccessTuple) MarshalJSON() ([]byte, error) {
|
||||
type AccessTuple struct {
|
||||
Address common.Address `json:"address" gencodec:"required"`
|
||||
StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"`
|
||||
}
|
||||
var enc AccessTuple
|
||||
enc.Address = a.Address
|
||||
enc.StorageKeys = a.StorageKeys
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (a *AccessTuple) UnmarshalJSON(input []byte) error {
|
||||
type AccessTuple struct {
|
||||
Address *common.Address `json:"address" gencodec:"required"`
|
||||
StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"`
|
||||
}
|
||||
var dec AccessTuple
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.Address == nil {
|
||||
return errors.New("missing required field 'address' for AccessTuple")
|
||||
}
|
||||
a.Address = *dec.Address
|
||||
if dec.StorageKeys == nil {
|
||||
return errors.New("missing required field 'storageKeys' for AccessTuple")
|
||||
}
|
||||
a.StorageKeys = dec.StorageKeys
|
||||
return nil
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ package types
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||
|
|
@ -12,8 +13,10 @@ import (
|
|||
|
||||
var _ = (*receiptMarshaling)(nil)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||
type Receipt struct {
|
||||
Type hexutil.Uint64 `json:"type,omitempty"`
|
||||
PostState hexutil.Bytes `json:"root"`
|
||||
Status hexutil.Uint `json:"status"`
|
||||
CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"`
|
||||
|
|
@ -22,8 +25,12 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
|||
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
|
||||
ContractAddress common.Address `json:"contractAddress"`
|
||||
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
BlockHash common.Hash `json:"blockHash,omitempty"`
|
||||
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
||||
TransactionIndex hexutil.Uint `json:"transactionIndex"`
|
||||
}
|
||||
var enc Receipt
|
||||
enc.Type = hexutil.Uint64(r.Type)
|
||||
enc.PostState = r.PostState
|
||||
enc.Status = hexutil.Uint(r.Status)
|
||||
enc.CumulativeGasUsed = hexutil.Uint64(r.CumulativeGasUsed)
|
||||
|
|
@ -32,11 +39,16 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
|||
enc.TxHash = r.TxHash
|
||||
enc.ContractAddress = r.ContractAddress
|
||||
enc.GasUsed = hexutil.Uint64(r.GasUsed)
|
||||
enc.BlockHash = r.BlockHash
|
||||
enc.BlockNumber = (*hexutil.Big)(r.BlockNumber)
|
||||
enc.TransactionIndex = hexutil.Uint(r.TransactionIndex)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||
type Receipt struct {
|
||||
Type *hexutil.Uint64 `json:"type,omitempty"`
|
||||
PostState *hexutil.Bytes `json:"root"`
|
||||
Status *hexutil.Uint `json:"status"`
|
||||
CumulativeGasUsed *hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"`
|
||||
|
|
@ -45,11 +57,17 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
|||
TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
|
||||
ContractAddress *common.Address `json:"contractAddress"`
|
||||
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
BlockHash *common.Hash `json:"blockHash,omitempty"`
|
||||
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
||||
TransactionIndex *hexutil.Uint `json:"transactionIndex"`
|
||||
}
|
||||
var dec Receipt
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.Type != nil {
|
||||
r.Type = uint8(*dec.Type)
|
||||
}
|
||||
if dec.PostState != nil {
|
||||
r.PostState = *dec.PostState
|
||||
}
|
||||
|
|
@ -79,5 +97,14 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
|||
return errors.New("missing required field 'gasUsed' for Receipt")
|
||||
}
|
||||
r.GasUsed = uint64(*dec.GasUsed)
|
||||
if dec.BlockHash != nil {
|
||||
r.BlockHash = *dec.BlockHash
|
||||
}
|
||||
if dec.BlockNumber != nil {
|
||||
r.BlockNumber = (*big.Int)(dec.BlockNumber)
|
||||
}
|
||||
if dec.TransactionIndex != nil {
|
||||
r.TransactionIndex = uint(*dec.TransactionIndex)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,99 +0,0 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||
)
|
||||
|
||||
var _ = (*txdataMarshaling)(nil)
|
||||
|
||||
func (t txdata) MarshalJSON() ([]byte, error) {
|
||||
type txdata struct {
|
||||
AccountNonce hexutil.Uint64 `json:"nonce" gencodec:"required"`
|
||||
Price *hexutil.Big `json:"gasPrice" gencodec:"required"`
|
||||
GasLimit hexutil.Uint64 `json:"gas" gencodec:"required"`
|
||||
Recipient *common.Address `json:"to" rlp:"nil"`
|
||||
Amount *hexutil.Big `json:"value" gencodec:"required"`
|
||||
Payload hexutil.Bytes `json:"input" gencodec:"required"`
|
||||
V *hexutil.Big `json:"v" gencodec:"required"`
|
||||
R *hexutil.Big `json:"r" gencodec:"required"`
|
||||
S *hexutil.Big `json:"s" gencodec:"required"`
|
||||
Hash *common.Hash `json:"hash" rlp:"-"`
|
||||
}
|
||||
var enc txdata
|
||||
enc.AccountNonce = hexutil.Uint64(t.AccountNonce)
|
||||
enc.Price = (*hexutil.Big)(t.Price)
|
||||
enc.GasLimit = hexutil.Uint64(t.GasLimit)
|
||||
enc.Recipient = t.Recipient
|
||||
enc.Amount = (*hexutil.Big)(t.Amount)
|
||||
enc.Payload = t.Payload
|
||||
enc.V = (*hexutil.Big)(t.V)
|
||||
enc.R = (*hexutil.Big)(t.R)
|
||||
enc.S = (*hexutil.Big)(t.S)
|
||||
enc.Hash = t.Hash
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
func (t *txdata) UnmarshalJSON(input []byte) error {
|
||||
type txdata struct {
|
||||
AccountNonce *hexutil.Uint64 `json:"nonce" gencodec:"required"`
|
||||
Price *hexutil.Big `json:"gasPrice" gencodec:"required"`
|
||||
GasLimit *hexutil.Uint64 `json:"gas" gencodec:"required"`
|
||||
Recipient *common.Address `json:"to" rlp:"nil"`
|
||||
Amount *hexutil.Big `json:"value" gencodec:"required"`
|
||||
Payload *hexutil.Bytes `json:"input" gencodec:"required"`
|
||||
V *hexutil.Big `json:"v" gencodec:"required"`
|
||||
R *hexutil.Big `json:"r" gencodec:"required"`
|
||||
S *hexutil.Big `json:"s" gencodec:"required"`
|
||||
Hash *common.Hash `json:"hash" rlp:"-"`
|
||||
}
|
||||
var dec txdata
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.AccountNonce == nil {
|
||||
return errors.New("missing required field 'nonce' for txdata")
|
||||
}
|
||||
t.AccountNonce = uint64(*dec.AccountNonce)
|
||||
if dec.Price == nil {
|
||||
return errors.New("missing required field 'gasPrice' for txdata")
|
||||
}
|
||||
t.Price = (*big.Int)(dec.Price)
|
||||
if dec.GasLimit == nil {
|
||||
return errors.New("missing required field 'gas' for txdata")
|
||||
}
|
||||
t.GasLimit = uint64(*dec.GasLimit)
|
||||
if dec.Recipient != nil {
|
||||
t.Recipient = dec.Recipient
|
||||
}
|
||||
if dec.Amount == nil {
|
||||
return errors.New("missing required field 'value' for txdata")
|
||||
}
|
||||
t.Amount = (*big.Int)(dec.Amount)
|
||||
if dec.Payload == nil {
|
||||
return errors.New("missing required field 'input' for txdata")
|
||||
}
|
||||
t.Payload = *dec.Payload
|
||||
if dec.V == nil {
|
||||
return errors.New("missing required field 'v' for txdata")
|
||||
}
|
||||
t.V = (*big.Int)(dec.V)
|
||||
if dec.R == nil {
|
||||
return errors.New("missing required field 'r' for txdata")
|
||||
}
|
||||
t.R = (*big.Int)(dec.R)
|
||||
if dec.S == nil {
|
||||
return errors.New("missing required field 's' for txdata")
|
||||
}
|
||||
t.S = (*big.Int)(dec.S)
|
||||
if dec.Hash != nil {
|
||||
t.Hash = dec.Hash
|
||||
}
|
||||
return nil
|
||||
}
|
||||
59
core/types/hashing.go
Normal file
59
core/types/hashing.go
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2014 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 types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// hasherPool holds LegacyKeccak256 hashers for rlpHash.
|
||||
var hasherPool = sync.Pool{
|
||||
New: func() interface{} { return sha3.NewLegacyKeccak256() },
|
||||
}
|
||||
|
||||
// deriveBufferPool holds temporary encoder buffers for DeriveSha and TX encoding.
|
||||
var encodeBufferPool = sync.Pool{
|
||||
New: func() interface{} { return new(bytes.Buffer) },
|
||||
}
|
||||
|
||||
// rlpHash encodes x and hashes the encoded bytes.
|
||||
func rlpHash(x interface{}) (h common.Hash) {
|
||||
sha := hasherPool.Get().(crypto.KeccakState)
|
||||
defer hasherPool.Put(sha)
|
||||
sha.Reset()
|
||||
rlp.Encode(sha, x)
|
||||
sha.Read(h[:])
|
||||
return h
|
||||
}
|
||||
|
||||
// prefixedRlpHash writes the prefix into the hasher before rlp-encoding x.
|
||||
// It's used for typed transactions.
|
||||
func prefixedRlpHash(prefix byte, x interface{}) (h common.Hash) {
|
||||
sha := hasherPool.Get().(crypto.KeccakState)
|
||||
defer hasherPool.Put(sha)
|
||||
sha.Reset()
|
||||
sha.Write([]byte{prefix})
|
||||
rlp.Encode(sha, x)
|
||||
sha.Read(h[:])
|
||||
return h
|
||||
}
|
||||
111
core/types/legacy_tx.go
Normal file
111
core/types/legacy_tx.go
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright 2020 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 types
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
)
|
||||
|
||||
// LegacyTx is the transaction data of regular Ethereum transactions.
|
||||
type LegacyTx struct {
|
||||
Nonce uint64 // nonce of sender account
|
||||
GasPrice *big.Int // wei per gas
|
||||
Gas uint64 // gas limit
|
||||
To *common.Address `rlp:"nil"` // nil means contract creation
|
||||
Value *big.Int // wei amount
|
||||
Data []byte // contract invocation input data
|
||||
V, R, S *big.Int // signature values
|
||||
}
|
||||
|
||||
// NewTransaction creates an unsigned legacy transaction.
|
||||
// Deprecated: use NewTx instead.
|
||||
func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
|
||||
return NewTx(&LegacyTx{
|
||||
Nonce: nonce,
|
||||
To: &to,
|
||||
Value: amount,
|
||||
Gas: gasLimit,
|
||||
GasPrice: gasPrice,
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// NewContractCreation creates an unsigned legacy transaction.
|
||||
// Deprecated: use NewTx instead.
|
||||
func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
|
||||
return NewTx(&LegacyTx{
|
||||
Nonce: nonce,
|
||||
Value: amount,
|
||||
Gas: gasLimit,
|
||||
GasPrice: gasPrice,
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// copy creates a deep copy of the transaction data and initializes all fields.
|
||||
func (tx *LegacyTx) copy() TxData {
|
||||
cpy := &LegacyTx{
|
||||
Nonce: tx.Nonce,
|
||||
To: tx.To, // TODO: copy pointed-to address
|
||||
Data: common.CopyBytes(tx.Data),
|
||||
Gas: tx.Gas,
|
||||
// These are initialized below.
|
||||
Value: new(big.Int),
|
||||
GasPrice: new(big.Int),
|
||||
V: new(big.Int),
|
||||
R: new(big.Int),
|
||||
S: new(big.Int),
|
||||
}
|
||||
if tx.Value != nil {
|
||||
cpy.Value.Set(tx.Value)
|
||||
}
|
||||
if tx.GasPrice != nil {
|
||||
cpy.GasPrice.Set(tx.GasPrice)
|
||||
}
|
||||
if tx.V != nil {
|
||||
cpy.V.Set(tx.V)
|
||||
}
|
||||
if tx.R != nil {
|
||||
cpy.R.Set(tx.R)
|
||||
}
|
||||
if tx.S != nil {
|
||||
cpy.S.Set(tx.S)
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
// accessors for innerTx.
|
||||
|
||||
func (tx *LegacyTx) txType() byte { return LegacyTxType }
|
||||
func (tx *LegacyTx) chainID() *big.Int { return deriveChainId(tx.V) }
|
||||
func (tx *LegacyTx) accessList() AccessList { return nil }
|
||||
func (tx *LegacyTx) data() []byte { return tx.Data }
|
||||
func (tx *LegacyTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice }
|
||||
func (tx *LegacyTx) value() *big.Int { return tx.Value }
|
||||
func (tx *LegacyTx) nonce() uint64 { return tx.Nonce }
|
||||
func (tx *LegacyTx) to() *common.Address { return tx.To }
|
||||
|
||||
func (tx *LegacyTx) rawSignatureValues() (v, r, s *big.Int) {
|
||||
return tx.V, tx.R, tx.S
|
||||
}
|
||||
|
||||
func (tx *LegacyTx) setSignatureValues(chainID, v, r, s *big.Int) {
|
||||
tx.V, tx.R, tx.S = v, r, s
|
||||
}
|
||||
|
|
@ -18,8 +18,10 @@ package types
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"unsafe"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
|
|
@ -34,6 +36,9 @@ var (
|
|||
receiptStatusSuccessfulRLP = []byte{0x01}
|
||||
)
|
||||
|
||||
// This error is returned when a typed receipt is decoded, but the string is empty.
|
||||
var errEmptyTypedReceipt = errors.New("empty typed receipt bytes")
|
||||
|
||||
const (
|
||||
// ReceiptStatusFailed is the status code of a transaction if execution failed.
|
||||
ReceiptStatusFailed = uint(0)
|
||||
|
|
@ -44,24 +49,35 @@ const (
|
|||
|
||||
// Receipt represents the results of a transaction.
|
||||
type Receipt struct {
|
||||
// Consensus fields
|
||||
// Consensus fields: These fields are defined by the Yellow Paper
|
||||
Type uint8 `json:"type,omitempty"`
|
||||
PostState []byte `json:"root"`
|
||||
Status uint `json:"status"`
|
||||
CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"`
|
||||
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Logs []*Log `json:"logs" gencodec:"required"`
|
||||
|
||||
// Implementation fields (don't reorder!)
|
||||
// Implementation fields: These fields are added by geth when processing a transaction.
|
||||
// They are stored in the chain database.
|
||||
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
|
||||
ContractAddress common.Address `json:"contractAddress"`
|
||||
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
||||
|
||||
// Inclusion information: These fields provide information about the inclusion of the
|
||||
// transaction corresponding to this receipt.
|
||||
BlockHash common.Hash `json:"blockHash,omitempty"`
|
||||
BlockNumber *big.Int `json:"blockNumber,omitempty"`
|
||||
TransactionIndex uint `json:"transactionIndex"`
|
||||
}
|
||||
|
||||
type receiptMarshaling struct {
|
||||
Type hexutil.Uint64
|
||||
PostState hexutil.Bytes
|
||||
Status hexutil.Uint
|
||||
CumulativeGasUsed hexutil.Uint64
|
||||
GasUsed hexutil.Uint64
|
||||
BlockNumber *hexutil.Big
|
||||
TransactionIndex hexutil.Uint
|
||||
}
|
||||
|
||||
// receiptRLP is the consensus encoding of a receipt.
|
||||
|
|
@ -72,7 +88,18 @@ type receiptRLP struct {
|
|||
Logs []*Log
|
||||
}
|
||||
|
||||
type receiptStorageRLP struct {
|
||||
// v4StoredReceiptRLP is the storage encoding of a receipt used in database version 4.
|
||||
type v4StoredReceiptRLP struct {
|
||||
PostStateOrStatus []byte
|
||||
CumulativeGasUsed uint64
|
||||
TxHash common.Hash
|
||||
ContractAddress common.Address
|
||||
Logs []*LogForStorage
|
||||
GasUsed uint64
|
||||
}
|
||||
|
||||
// v3StoredReceiptRLP is the original storage encoding of a receipt including some unnecessary fields.
|
||||
type v3StoredReceiptRLP struct {
|
||||
PostStateOrStatus []byte
|
||||
CumulativeGasUsed uint64
|
||||
Bloom Bloom
|
||||
|
|
@ -83,8 +110,13 @@ type receiptStorageRLP struct {
|
|||
}
|
||||
|
||||
// NewReceipt creates a barebone transaction receipt, copying the init fields.
|
||||
// Deprecated: create receipts using a struct literal instead.
|
||||
func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt {
|
||||
r := &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: cumulativeGasUsed}
|
||||
r := &Receipt{
|
||||
Type: LegacyTxType,
|
||||
PostState: common.CopyBytes(root),
|
||||
CumulativeGasUsed: cumulativeGasUsed,
|
||||
}
|
||||
if failed {
|
||||
r.Status = ReceiptStatusFailed
|
||||
} else {
|
||||
|
|
@ -96,21 +128,65 @@ func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt {
|
|||
// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt
|
||||
// into an RLP stream. If no post state is present, byzantium fork is assumed.
|
||||
func (r *Receipt) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs})
|
||||
data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}
|
||||
if r.Type == LegacyTxType {
|
||||
return rlp.Encode(w, data)
|
||||
}
|
||||
// It's an EIP-2718 typed TX receipt.
|
||||
if r.Type != AccessListTxType {
|
||||
return ErrTxTypeNotSupported
|
||||
}
|
||||
buf := encodeBufferPool.Get().(*bytes.Buffer)
|
||||
defer encodeBufferPool.Put(buf)
|
||||
buf.Reset()
|
||||
buf.WriteByte(r.Type)
|
||||
if err := rlp.Encode(buf, data); err != nil {
|
||||
return err
|
||||
}
|
||||
return rlp.Encode(w, buf.Bytes())
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
|
||||
// from an RLP stream.
|
||||
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
|
||||
var dec receiptRLP
|
||||
if err := s.Decode(&dec); err != nil {
|
||||
kind, _, err := s.Kind()
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
case kind == rlp.List:
|
||||
// It's a legacy receipt.
|
||||
var dec receiptRLP
|
||||
if err := s.Decode(&dec); err != nil {
|
||||
return err
|
||||
}
|
||||
r.Type = LegacyTxType
|
||||
return r.setFromRLP(dec)
|
||||
case kind == rlp.String:
|
||||
// It's an EIP-2718 typed tx receipt.
|
||||
b, err := s.Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return errEmptyTypedReceipt
|
||||
}
|
||||
r.Type = b[0]
|
||||
if r.Type == AccessListTxType {
|
||||
var dec receiptRLP
|
||||
if err := rlp.DecodeBytes(b[1:], &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.setFromRLP(dec)
|
||||
}
|
||||
return ErrTxTypeNotSupported
|
||||
default:
|
||||
return rlp.ErrExpectedList
|
||||
}
|
||||
if err := r.setStatus(dec.PostStateOrStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
r.CumulativeGasUsed, r.Bloom, r.Logs = dec.CumulativeGasUsed, dec.Bloom, dec.Logs
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Receipt) setFromRLP(data receiptRLP) error {
|
||||
r.CumulativeGasUsed, r.Bloom, r.Logs = data.CumulativeGasUsed, data.Bloom, data.Logs
|
||||
return r.setStatus(data.PostStateOrStatus)
|
||||
}
|
||||
|
||||
func (r *Receipt) setStatus(postStateOrStatus []byte) error {
|
||||
|
|
@ -141,7 +217,6 @@ func (r *Receipt) statusEncoding() []byte {
|
|||
// to approximate and limit the memory consumption of various caches.
|
||||
func (r *Receipt) Size() common.StorageSize {
|
||||
size := common.StorageSize(unsafe.Sizeof(*r)) + common.StorageSize(len(r.PostState))
|
||||
|
||||
size += common.StorageSize(len(r.Logs)) * common.StorageSize(unsafe.Sizeof(Log{}))
|
||||
for _, log := range r.Logs {
|
||||
size += common.StorageSize(len(log.Topics)*common.HashLength + len(log.Data))
|
||||
|
|
@ -164,7 +239,7 @@ type ReceiptForStorage Receipt
|
|||
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
|
||||
// into an RLP stream.
|
||||
func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
|
||||
enc := &receiptStorageRLP{
|
||||
enc := &v3StoredReceiptRLP{
|
||||
PostStateOrStatus: (*Receipt)(r).statusEncoding(),
|
||||
CumulativeGasUsed: r.CumulativeGasUsed,
|
||||
Bloom: r.Bloom,
|
||||
|
|
@ -182,25 +257,64 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
|
|||
// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation
|
||||
// fields of a receipt from an RLP stream.
|
||||
func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
|
||||
var dec receiptStorageRLP
|
||||
if err := s.Decode(&dec); err != nil {
|
||||
// Retrieve the entire receipt blob as we need to try multiple decoders
|
||||
blob, err := s.Raw()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := (*Receipt)(r).setStatus(dec.PostStateOrStatus); err != nil {
|
||||
// Try decoding from the newest format for future proofness, then the older one
|
||||
// for old nodes that just upgraded. V4 was an intermediate unreleased format so
|
||||
// we do need to decode it, but it's not common (try last).
|
||||
if err := decodeV3StoredReceiptRLP(r, blob); err == nil {
|
||||
return nil
|
||||
}
|
||||
return decodeV4StoredReceiptRLP(r, blob)
|
||||
}
|
||||
|
||||
func decodeV3StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
|
||||
var stored v3StoredReceiptRLP
|
||||
if err := rlp.DecodeBytes(blob, &stored); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
// Assign the consensus fields
|
||||
r.CumulativeGasUsed, r.Bloom = dec.CumulativeGasUsed, dec.Bloom
|
||||
r.Logs = make([]*Log, len(dec.Logs))
|
||||
for i, log := range dec.Logs {
|
||||
r.CumulativeGasUsed = stored.CumulativeGasUsed
|
||||
r.Bloom = stored.Bloom
|
||||
r.Logs = make([]*Log, len(stored.Logs))
|
||||
for i, log := range stored.Logs {
|
||||
r.Logs[i] = (*Log)(log)
|
||||
}
|
||||
// Assign the implementation fields
|
||||
r.TxHash, r.ContractAddress, r.GasUsed = dec.TxHash, dec.ContractAddress, dec.GasUsed
|
||||
r.TxHash = stored.TxHash
|
||||
r.ContractAddress = stored.ContractAddress
|
||||
r.GasUsed = stored.GasUsed
|
||||
return nil
|
||||
}
|
||||
|
||||
// Receipts is a wrapper around a Receipt array to implement DerivableList.
|
||||
func decodeV4StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
|
||||
var stored v4StoredReceiptRLP
|
||||
if err := rlp.DecodeBytes(blob, &stored); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
r.CumulativeGasUsed = stored.CumulativeGasUsed
|
||||
r.TxHash = stored.TxHash
|
||||
r.ContractAddress = stored.ContractAddress
|
||||
r.GasUsed = stored.GasUsed
|
||||
r.Logs = make([]*Log, len(stored.Logs))
|
||||
for i, log := range stored.Logs {
|
||||
r.Logs[i] = (*Log)(log)
|
||||
}
|
||||
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Receipts implements DerivableList for receipts.
|
||||
type Receipts []*Receipt
|
||||
|
||||
// Len returns the number of receipts in this list.
|
||||
|
|
|
|||
170
core/types/receipt_test.go
Normal file
170
core/types/receipt_test.go
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
// Copyright 2019 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 types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
)
|
||||
|
||||
func TestDecodeEmptyTypedReceipt(t *testing.T) {
|
||||
input := []byte{0x80}
|
||||
var r Receipt
|
||||
err := rlp.DecodeBytes(input, &r)
|
||||
if err != errEmptyTypedReceipt {
|
||||
t.Fatal("wrong error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLegacyReceiptDecoding(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
encode func(*Receipt) ([]byte, error)
|
||||
}{
|
||||
{
|
||||
"V4StoredReceiptRLP",
|
||||
encodeAsV4StoredReceiptRLP,
|
||||
},
|
||||
{
|
||||
"V3StoredReceiptRLP",
|
||||
encodeAsV3StoredReceiptRLP,
|
||||
},
|
||||
}
|
||||
|
||||
tx := NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)
|
||||
receipt := &Receipt{
|
||||
Status: ReceiptStatusFailed,
|
||||
CumulativeGasUsed: 1,
|
||||
Logs: []*Log{
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x01, 0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
},
|
||||
TxHash: tx.Hash(),
|
||||
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
|
||||
GasUsed: 111111,
|
||||
}
|
||||
receipt.Bloom = CreateBloom(Receipts{receipt})
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
enc, err := tc.encode(receipt)
|
||||
if err != nil {
|
||||
t.Fatalf("Error encoding receipt: %v", err)
|
||||
}
|
||||
var dec ReceiptForStorage
|
||||
if err := rlp.DecodeBytes(enc, &dec); err != nil {
|
||||
t.Fatalf("Error decoding RLP receipt: %v", err)
|
||||
}
|
||||
// Check whether all consensus fields are correct.
|
||||
if dec.Status != receipt.Status {
|
||||
t.Fatalf("Receipt status mismatch, want %v, have %v", receipt.Status, dec.Status)
|
||||
}
|
||||
if dec.CumulativeGasUsed != receipt.CumulativeGasUsed {
|
||||
t.Fatalf("Receipt CumulativeGasUsed mismatch, want %v, have %v", receipt.CumulativeGasUsed, dec.CumulativeGasUsed)
|
||||
}
|
||||
if dec.Bloom != receipt.Bloom {
|
||||
t.Fatalf("Bloom data mismatch, want %v, have %v", receipt.Bloom, dec.Bloom)
|
||||
}
|
||||
if len(dec.Logs) != len(receipt.Logs) {
|
||||
t.Fatalf("Receipt log number mismatch, want %v, have %v", len(receipt.Logs), len(dec.Logs))
|
||||
}
|
||||
for i := 0; i < len(dec.Logs); i++ {
|
||||
if dec.Logs[i].Address != receipt.Logs[i].Address {
|
||||
t.Fatalf("Receipt log %d address mismatch, want %v, have %v", i, receipt.Logs[i].Address, dec.Logs[i].Address)
|
||||
}
|
||||
if !reflect.DeepEqual(dec.Logs[i].Topics, receipt.Logs[i].Topics) {
|
||||
t.Fatalf("Receipt log %d topics mismatch, want %v, have %v", i, receipt.Logs[i].Topics, dec.Logs[i].Topics)
|
||||
}
|
||||
if !bytes.Equal(dec.Logs[i].Data, receipt.Logs[i].Data) {
|
||||
t.Fatalf("Receipt log %d data mismatch, want %v, have %v", i, receipt.Logs[i].Data, dec.Logs[i].Data)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func encodeAsV4StoredReceiptRLP(want *Receipt) ([]byte, error) {
|
||||
stored := &v4StoredReceiptRLP{
|
||||
PostStateOrStatus: want.statusEncoding(),
|
||||
CumulativeGasUsed: want.CumulativeGasUsed,
|
||||
TxHash: want.TxHash,
|
||||
ContractAddress: want.ContractAddress,
|
||||
Logs: make([]*LogForStorage, len(want.Logs)),
|
||||
GasUsed: want.GasUsed,
|
||||
}
|
||||
for i, log := range want.Logs {
|
||||
stored.Logs[i] = (*LogForStorage)(log)
|
||||
}
|
||||
return rlp.EncodeToBytes(stored)
|
||||
}
|
||||
|
||||
func encodeAsV3StoredReceiptRLP(want *Receipt) ([]byte, error) {
|
||||
stored := &v3StoredReceiptRLP{
|
||||
PostStateOrStatus: want.statusEncoding(),
|
||||
CumulativeGasUsed: want.CumulativeGasUsed,
|
||||
Bloom: want.Bloom,
|
||||
TxHash: want.TxHash,
|
||||
ContractAddress: want.ContractAddress,
|
||||
Logs: make([]*LogForStorage, len(want.Logs)),
|
||||
GasUsed: want.GasUsed,
|
||||
}
|
||||
for i, log := range want.Logs {
|
||||
stored.Logs[i] = (*LogForStorage)(log)
|
||||
}
|
||||
return rlp.EncodeToBytes(stored)
|
||||
}
|
||||
|
||||
// TestTypedReceiptEncodingDecoding reproduces a flaw that existed in the receipt
|
||||
// rlp decoder, which failed due to a shadowing error.
|
||||
func TestTypedReceiptEncodingDecoding(t *testing.T) {
|
||||
var payload = common.FromHex("f9043eb9010c01f90108018262d4b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0b9010c01f901080182cd14b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0b9010d01f901090183013754b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0b9010d01f90109018301a194b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0")
|
||||
check := func(bundle []*Receipt) {
|
||||
t.Helper()
|
||||
for i, receipt := range bundle {
|
||||
if got, want := receipt.Type, uint8(1); got != want {
|
||||
t.Fatalf("bundle %d: got %x, want %x", i, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
var bundle []*Receipt
|
||||
rlp.DecodeBytes(payload, &bundle)
|
||||
check(bundle)
|
||||
}
|
||||
{
|
||||
var bundle []*Receipt
|
||||
r := bytes.NewReader(payload)
|
||||
s := rlp.NewStream(r, uint64(len(payload)))
|
||||
if err := s.Decode(&bundle); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
check(bundle)
|
||||
}
|
||||
}
|
||||
|
|
@ -24,9 +24,9 @@ import (
|
|||
"io"
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
)
|
||||
|
|
@ -35,6 +35,15 @@ import (
|
|||
|
||||
var (
|
||||
ErrInvalidSig = errors.New("invalid transaction v, r, s values")
|
||||
ErrUnexpectedProtection = errors.New("transaction type does not supported EIP-155 protected signatures")
|
||||
ErrInvalidTxType = errors.New("transaction type not valid in this context")
|
||||
ErrTxTypeNotSupported = errors.New("transaction type not supported")
|
||||
ErrGasFeeCapTooLow = errors.New("fee cap less than base fee")
|
||||
errShortTypedTx = errors.New("typed transaction too short")
|
||||
errInvalidYParity = errors.New("'yParity' field must be 0 or 1")
|
||||
errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match")
|
||||
errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction")
|
||||
errEmptyTypedTx = errors.New("empty typed transaction bytes")
|
||||
errNoSigner = errors.New("missing signing methods")
|
||||
skipNonceDestinationAddress = map[string]bool{
|
||||
common.XDCXAddr: true,
|
||||
|
|
@ -44,221 +53,318 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
// deriveSigner makes a *best* guess about which signer to use.
|
||||
func deriveSigner(V *big.Int) Signer {
|
||||
if V.Sign() != 0 && isProtectedV(V) {
|
||||
return NewEIP155Signer(deriveChainId(V))
|
||||
} else {
|
||||
return HomesteadSigner{}
|
||||
}
|
||||
}
|
||||
// Transaction types.
|
||||
const (
|
||||
LegacyTxType = iota
|
||||
AccessListTxType
|
||||
)
|
||||
|
||||
// Transaction is an Ethereum transaction.
|
||||
type Transaction struct {
|
||||
data txdata
|
||||
inner TxData // Consensus contents of a transaction
|
||||
time time.Time // Time first seen locally (spam avoidance)
|
||||
|
||||
// caches
|
||||
hash atomic.Value
|
||||
size atomic.Value
|
||||
from atomic.Value
|
||||
}
|
||||
|
||||
type txdata struct {
|
||||
AccountNonce uint64 `json:"nonce" gencodec:"required"`
|
||||
Price *big.Int `json:"gasPrice" gencodec:"required"`
|
||||
GasLimit uint64 `json:"gas" gencodec:"required"`
|
||||
Recipient *common.Address `json:"to" rlp:"nil"` // nil means contract creation
|
||||
Amount *big.Int `json:"value" gencodec:"required"`
|
||||
Payload []byte `json:"input" gencodec:"required"`
|
||||
|
||||
// Signature values
|
||||
V *big.Int `json:"v" gencodec:"required"`
|
||||
R *big.Int `json:"r" gencodec:"required"`
|
||||
S *big.Int `json:"s" gencodec:"required"`
|
||||
|
||||
// This is only used when marshaling to JSON.
|
||||
Hash *common.Hash `json:"hash" rlp:"-"`
|
||||
// NewTx creates a new transaction.
|
||||
func NewTx(inner TxData) *Transaction {
|
||||
tx := new(Transaction)
|
||||
tx.setDecoded(inner.copy(), 0)
|
||||
return tx
|
||||
}
|
||||
|
||||
type txdataMarshaling struct {
|
||||
AccountNonce hexutil.Uint64
|
||||
Price *hexutil.Big
|
||||
GasLimit hexutil.Uint64
|
||||
Amount *hexutil.Big
|
||||
Payload hexutil.Bytes
|
||||
V *hexutil.Big
|
||||
R *hexutil.Big
|
||||
S *hexutil.Big
|
||||
// TxData is the underlying data of a transaction.
|
||||
//
|
||||
// This is implemented by LegacyTx and AccessListTx.
|
||||
type TxData interface {
|
||||
txType() byte // returns the type ID
|
||||
copy() TxData // creates a deep copy and initializes all fields
|
||||
|
||||
chainID() *big.Int
|
||||
accessList() AccessList
|
||||
data() []byte
|
||||
gas() uint64
|
||||
gasPrice() *big.Int
|
||||
value() *big.Int
|
||||
nonce() uint64
|
||||
to() *common.Address
|
||||
|
||||
rawSignatureValues() (v, r, s *big.Int)
|
||||
setSignatureValues(chainID, v, r, s *big.Int)
|
||||
}
|
||||
|
||||
func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
|
||||
return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data)
|
||||
}
|
||||
|
||||
func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
|
||||
return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data)
|
||||
}
|
||||
|
||||
func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
|
||||
if len(data) > 0 {
|
||||
data = common.CopyBytes(data)
|
||||
// EncodeRLP implements rlp.Encoder
|
||||
func (tx *Transaction) EncodeRLP(w io.Writer) error {
|
||||
if tx.Type() == LegacyTxType {
|
||||
return rlp.Encode(w, tx.inner)
|
||||
}
|
||||
d := txdata{
|
||||
AccountNonce: nonce,
|
||||
Recipient: to,
|
||||
Payload: data,
|
||||
Amount: new(big.Int),
|
||||
GasLimit: gasLimit,
|
||||
Price: new(big.Int),
|
||||
V: new(big.Int),
|
||||
R: new(big.Int),
|
||||
S: new(big.Int),
|
||||
// It's an EIP-2718 typed TX envelope.
|
||||
buf := encodeBufferPool.Get().(*bytes.Buffer)
|
||||
defer encodeBufferPool.Put(buf)
|
||||
buf.Reset()
|
||||
if err := tx.encodeTyped(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
if amount != nil {
|
||||
d.Amount.Set(amount)
|
||||
return rlp.Encode(w, buf.Bytes())
|
||||
}
|
||||
|
||||
// encodeTyped writes the canonical encoding of a typed transaction to w.
|
||||
func (tx *Transaction) encodeTyped(w *bytes.Buffer) error {
|
||||
w.WriteByte(tx.Type())
|
||||
return rlp.Encode(w, tx.inner)
|
||||
}
|
||||
|
||||
// MarshalBinary returns the canonical encoding of the transaction.
|
||||
// For legacy transactions, it returns the RLP encoding. For EIP-2718 typed
|
||||
// transactions, it returns the type and payload.
|
||||
func (tx *Transaction) MarshalBinary() ([]byte, error) {
|
||||
if tx.Type() == LegacyTxType {
|
||||
return rlp.EncodeToBytes(tx.inner)
|
||||
}
|
||||
if gasPrice != nil {
|
||||
d.Price.Set(gasPrice)
|
||||
var buf bytes.Buffer
|
||||
err := tx.encodeTyped(&buf)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder
|
||||
func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
||||
kind, size, err := s.Kind()
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
case kind == rlp.List:
|
||||
// It's a legacy transaction.
|
||||
var inner LegacyTx
|
||||
err := s.Decode(&inner)
|
||||
if err == nil {
|
||||
tx.setDecoded(&inner, int(rlp.ListSize(size)))
|
||||
}
|
||||
return err
|
||||
case kind == rlp.String:
|
||||
// It's an EIP-2718 typed TX envelope.
|
||||
var b []byte
|
||||
if b, err = s.Bytes(); err != nil {
|
||||
return err
|
||||
}
|
||||
inner, err := tx.decodeTyped(b)
|
||||
if err == nil {
|
||||
tx.setDecoded(inner, len(b))
|
||||
}
|
||||
return err
|
||||
default:
|
||||
return rlp.ErrExpectedList
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the canonical encoding of transactions.
|
||||
// It supports legacy RLP transactions and EIP2718 typed transactions.
|
||||
func (tx *Transaction) UnmarshalBinary(b []byte) error {
|
||||
if len(b) > 0 && b[0] > 0x7f {
|
||||
// It's a legacy transaction.
|
||||
var data LegacyTx
|
||||
err := rlp.DecodeBytes(b, &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tx.setDecoded(&data, len(b))
|
||||
return nil
|
||||
}
|
||||
// It's an EIP2718 typed transaction envelope.
|
||||
inner, err := tx.decodeTyped(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tx.setDecoded(inner, len(b))
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeTyped decodes a typed transaction from the canonical format.
|
||||
func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
|
||||
if len(b) == 0 {
|
||||
return nil, errEmptyTypedTx
|
||||
}
|
||||
switch b[0] {
|
||||
case AccessListTxType:
|
||||
var inner AccessListTx
|
||||
err := rlp.DecodeBytes(b[1:], &inner)
|
||||
return &inner, err
|
||||
default:
|
||||
return nil, ErrTxTypeNotSupported
|
||||
}
|
||||
}
|
||||
|
||||
// setDecoded sets the inner transaction and size after decoding.
|
||||
func (tx *Transaction) setDecoded(inner TxData, size int) {
|
||||
tx.inner = inner
|
||||
tx.time = time.Now()
|
||||
if size > 0 {
|
||||
tx.size.Store(common.StorageSize(size))
|
||||
}
|
||||
}
|
||||
|
||||
func sanityCheckSignature(v *big.Int, r *big.Int, s *big.Int, maybeProtected bool) error {
|
||||
if isProtectedV(v) && !maybeProtected {
|
||||
return ErrUnexpectedProtection
|
||||
}
|
||||
|
||||
return &Transaction{data: d}
|
||||
}
|
||||
var plainV byte
|
||||
if isProtectedV(v) {
|
||||
chainID := deriveChainId(v).Uint64()
|
||||
plainV = byte(v.Uint64() - 35 - 2*chainID)
|
||||
} else if maybeProtected {
|
||||
// Only EIP-155 signatures can be optionally protected. Since
|
||||
// we determined this v value is not protected, it must be a
|
||||
// raw 27 or 28.
|
||||
plainV = byte(v.Uint64() - 27)
|
||||
} else {
|
||||
// If the signature is not optionally protected, we assume it
|
||||
// must already be equal to the recovery id.
|
||||
plainV = byte(v.Uint64())
|
||||
}
|
||||
if !crypto.ValidateSignatureValues(plainV, r, s, false) {
|
||||
return ErrInvalidSig
|
||||
}
|
||||
|
||||
// ChainId returns which chain id this transaction was signed for (if at all)
|
||||
func (tx *Transaction) ChainId() *big.Int {
|
||||
return deriveChainId(tx.data.V)
|
||||
}
|
||||
|
||||
// Protected returns whether the transaction is protected from replay protection.
|
||||
func (tx *Transaction) Protected() bool {
|
||||
return isProtectedV(tx.data.V)
|
||||
return nil
|
||||
}
|
||||
|
||||
func isProtectedV(V *big.Int) bool {
|
||||
if V.BitLen() <= 8 {
|
||||
v := V.Uint64()
|
||||
return v != 27 && v != 28
|
||||
return v != 27 && v != 28 && v != 1 && v != 0
|
||||
}
|
||||
// anything not 27 or 28 are considered unprotected
|
||||
// anything not 27 or 28 is considered protected
|
||||
return true
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder
|
||||
func (tx *Transaction) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, &tx.data)
|
||||
// Protected says whether the transaction is replay-protected.
|
||||
func (tx *Transaction) Protected() bool {
|
||||
switch tx := tx.inner.(type) {
|
||||
case *LegacyTx:
|
||||
return tx.V != nil && isProtectedV(tx.V)
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder
|
||||
func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
||||
_, size, _ := s.Kind()
|
||||
err := s.Decode(&tx.data)
|
||||
if err == nil {
|
||||
tx.size.Store(common.StorageSize(rlp.ListSize(size)))
|
||||
}
|
||||
|
||||
return err
|
||||
// Type returns the transaction type.
|
||||
func (tx *Transaction) Type() uint8 {
|
||||
return tx.inner.txType()
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the web3 RPC transaction format.
|
||||
func (tx *Transaction) MarshalJSON() ([]byte, error) {
|
||||
hash := tx.Hash()
|
||||
data := tx.data
|
||||
data.Hash = &hash
|
||||
return data.MarshalJSON()
|
||||
// ChainId returns the EIP155 chain ID of the transaction. The return value will always be
|
||||
// non-nil. For legacy transactions which are not replay-protected, the return value is
|
||||
// zero.
|
||||
func (tx *Transaction) ChainId() *big.Int {
|
||||
return tx.inner.chainID()
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes the web3 RPC transaction format.
|
||||
func (tx *Transaction) UnmarshalJSON(input []byte) error {
|
||||
var dec txdata
|
||||
if err := dec.UnmarshalJSON(input); err != nil {
|
||||
return err
|
||||
}
|
||||
var V byte
|
||||
if isProtectedV(dec.V) {
|
||||
chainID := deriveChainId(dec.V).Uint64()
|
||||
V = byte(dec.V.Uint64() - 35 - 2*chainID)
|
||||
} else {
|
||||
V = byte(dec.V.Uint64() - 27)
|
||||
}
|
||||
if !crypto.ValidateSignatureValues(V, dec.R, dec.S, false) {
|
||||
return ErrInvalidSig
|
||||
}
|
||||
*tx = Transaction{data: dec}
|
||||
return nil
|
||||
}
|
||||
// Data returns the input data of the transaction.
|
||||
func (tx *Transaction) Data() []byte { return tx.inner.data() }
|
||||
|
||||
func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
|
||||
func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit }
|
||||
func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) }
|
||||
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) }
|
||||
func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce }
|
||||
func (tx *Transaction) CheckNonce() bool { return true }
|
||||
// AccessList returns the access list of the transaction.
|
||||
func (tx *Transaction) AccessList() AccessList { return tx.inner.accessList() }
|
||||
|
||||
// Gas returns the gas limit of the transaction.
|
||||
func (tx *Transaction) Gas() uint64 { return tx.inner.gas() }
|
||||
|
||||
// GasPrice returns the gas price of the transaction.
|
||||
func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.inner.gasPrice()) }
|
||||
|
||||
// Value returns the ether amount of the transaction.
|
||||
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.inner.value()) }
|
||||
|
||||
// Nonce returns the sender account nonce of the transaction.
|
||||
func (tx *Transaction) Nonce() uint64 { return tx.inner.nonce() }
|
||||
|
||||
// To returns the recipient address of the transaction.
|
||||
// It returns nil if the transaction is a contract creation.
|
||||
// For contract-creation transactions, To returns nil.
|
||||
func (tx *Transaction) To() *common.Address {
|
||||
if tx.data.Recipient == nil {
|
||||
// Copy the pointed-to address.
|
||||
ito := tx.inner.to()
|
||||
if ito == nil {
|
||||
return nil
|
||||
}
|
||||
to := *tx.data.Recipient
|
||||
return &to
|
||||
cpy := *ito
|
||||
return &cpy
|
||||
}
|
||||
|
||||
func (tx *Transaction) From() *common.Address {
|
||||
if tx.data.V != nil {
|
||||
signer := deriveSigner(tx.data.V)
|
||||
if f, err := Sender(signer, tx); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
return &f
|
||||
}
|
||||
var signer Signer
|
||||
if tx.Protected() {
|
||||
signer = LatestSignerForChainID(tx.ChainId())
|
||||
} else {
|
||||
signer = HomesteadSigner{}
|
||||
}
|
||||
from, err := Sender(signer, tx)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &from
|
||||
}
|
||||
|
||||
// Hash hashes the RLP encoding of tx.
|
||||
// It uniquely identifies the transaction.
|
||||
// RawSignatureValues returns the V, R, S signature values of the transaction.
|
||||
// The return values should not be modified by the caller.
|
||||
func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) {
|
||||
return tx.inner.rawSignatureValues()
|
||||
}
|
||||
|
||||
// GasPriceCmp compares the gas prices of two transactions.
|
||||
func (tx *Transaction) GasPriceCmp(other *Transaction) int {
|
||||
return tx.inner.gasPrice().Cmp(other.inner.gasPrice())
|
||||
}
|
||||
|
||||
// GasPriceIntCmp compares the gas price of the transaction against the given price.
|
||||
func (tx *Transaction) GasPriceIntCmp(other *big.Int) int {
|
||||
return tx.inner.gasPrice().Cmp(other)
|
||||
}
|
||||
|
||||
// Hash returns the transaction hash.
|
||||
func (tx *Transaction) Hash() common.Hash {
|
||||
if hash := tx.hash.Load(); hash != nil {
|
||||
return hash.(common.Hash)
|
||||
}
|
||||
v := rlpHash(tx)
|
||||
tx.hash.Store(v)
|
||||
return v
|
||||
}
|
||||
|
||||
func (tx *Transaction) CacheHash() {
|
||||
v := rlpHash(tx)
|
||||
tx.hash.Store(v)
|
||||
var h common.Hash
|
||||
if tx.Type() == LegacyTxType {
|
||||
h = rlpHash(tx.inner)
|
||||
} else {
|
||||
h = prefixedRlpHash(tx.Type(), tx.inner)
|
||||
}
|
||||
tx.hash.Store(h)
|
||||
return h
|
||||
}
|
||||
|
||||
// Size returns the true RLP encoded storage size of the transaction, either by
|
||||
// encoding and returning it, or returning a previsouly cached value.
|
||||
// encoding and returning it, or returning a previously cached value.
|
||||
func (tx *Transaction) Size() common.StorageSize {
|
||||
if size := tx.size.Load(); size != nil {
|
||||
return size.(common.StorageSize)
|
||||
}
|
||||
c := writeCounter(0)
|
||||
rlp.Encode(&c, &tx.data)
|
||||
rlp.Encode(&c, &tx.inner)
|
||||
tx.size.Store(common.StorageSize(c))
|
||||
return common.StorageSize(c)
|
||||
}
|
||||
|
||||
// AsMessage returns the transaction as a core.Message.
|
||||
//
|
||||
// AsMessage requires a signer to derive the sender.
|
||||
//
|
||||
// XXX Rename message to something less arbitrary?
|
||||
func (tx *Transaction) AsMessage(s Signer, balanceFee *big.Int, number *big.Int) (Message, error) {
|
||||
msg := Message{
|
||||
nonce: tx.data.AccountNonce,
|
||||
gasLimit: tx.data.GasLimit,
|
||||
gasPrice: new(big.Int).Set(tx.data.Price),
|
||||
to: tx.data.Recipient,
|
||||
amount: tx.data.Amount,
|
||||
data: tx.data.Payload,
|
||||
nonce: tx.Nonce(),
|
||||
gasLimit: tx.Gas(),
|
||||
gasPrice: new(big.Int).Set(tx.GasPrice()),
|
||||
to: tx.To(),
|
||||
amount: tx.Value(),
|
||||
data: tx.Data(),
|
||||
accessList: tx.AccessList(),
|
||||
checkNonce: true,
|
||||
balanceTokenFee: balanceFee,
|
||||
}
|
||||
|
||||
var err error
|
||||
msg.from, err = Sender(s, tx)
|
||||
if balanceFee != nil {
|
||||
|
|
@ -274,35 +380,31 @@ func (tx *Transaction) AsMessage(s Signer, balanceFee *big.Int, number *big.Int)
|
|||
}
|
||||
|
||||
// WithSignature returns a new transaction with the given signature.
|
||||
// This signature needs to be formatted as described in the yellow paper (v+27).
|
||||
// This signature needs to be in the [R || S || V] format where V is 0 or 1.
|
||||
func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
|
||||
r, s, v, err := signer.SignatureValues(tx, sig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cpy := &Transaction{data: tx.data}
|
||||
cpy.data.R, cpy.data.S, cpy.data.V = r, s, v
|
||||
return cpy, nil
|
||||
cpy := tx.inner.copy()
|
||||
cpy.setSignatureValues(signer.ChainID(), v, r, s)
|
||||
return &Transaction{inner: cpy, time: tx.time}, nil
|
||||
}
|
||||
|
||||
// Cost returns amount + gasprice * gaslimit.
|
||||
// Cost returns gas * gasPrice + value.
|
||||
func (tx *Transaction) Cost() *big.Int {
|
||||
total := new(big.Int).Mul(tx.data.Price, new(big.Int).SetUint64(tx.data.GasLimit))
|
||||
total.Add(total, tx.data.Amount)
|
||||
total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas()))
|
||||
total.Add(total, tx.Value())
|
||||
return total
|
||||
}
|
||||
|
||||
// Cost returns amount + gasprice * gaslimit.
|
||||
// TxCost returns gas * gasPrice + value.
|
||||
func (tx *Transaction) TxCost(number *big.Int) *big.Int {
|
||||
total := new(big.Int).Mul(common.GetGasPrice(number), new(big.Int).SetUint64(tx.data.GasLimit))
|
||||
total.Add(total, tx.data.Amount)
|
||||
total := new(big.Int).Mul(common.GetGasPrice(number), new(big.Int).SetUint64(tx.Gas()))
|
||||
total.Add(total, tx.Value())
|
||||
return total
|
||||
}
|
||||
|
||||
func (tx *Transaction) RawSignatureValues() (*big.Int, *big.Int, *big.Int) {
|
||||
return tx.data.V, tx.data.R, tx.data.S
|
||||
}
|
||||
|
||||
func (tx *Transaction) IsSpecialTransaction() bool {
|
||||
if tx.To() == nil {
|
||||
return false
|
||||
|
|
@ -473,25 +575,24 @@ func (tx *Transaction) IsXDCZApplyTransaction() bool {
|
|||
|
||||
func (tx *Transaction) String() string {
|
||||
var from, to string
|
||||
if tx.data.V != nil {
|
||||
// make a best guess about the signer and use that to derive
|
||||
// the sender.
|
||||
signer := deriveSigner(tx.data.V)
|
||||
if f, err := Sender(signer, tx); err != nil { // derive but don't cache
|
||||
from = "[invalid sender: invalid sig]"
|
||||
} else {
|
||||
from = fmt.Sprintf("%x", f[:])
|
||||
}
|
||||
|
||||
sender := tx.From()
|
||||
if sender != nil {
|
||||
from = fmt.Sprintf("%x", sender[:])
|
||||
} else {
|
||||
from = "[invalid sender: nil V field]"
|
||||
from = "[invalid sender]"
|
||||
}
|
||||
|
||||
if tx.data.Recipient == nil {
|
||||
receiver := tx.To()
|
||||
if receiver == nil {
|
||||
to = "[contract creation]"
|
||||
} else {
|
||||
to = fmt.Sprintf("%x", tx.data.Recipient[:])
|
||||
to = fmt.Sprintf("%x", receiver[:])
|
||||
}
|
||||
enc, _ := rlp.EncodeToBytes(&tx.data)
|
||||
|
||||
enc, _ := rlp.EncodeToBytes(tx.Data())
|
||||
v, r, s := tx.RawSignatureValues()
|
||||
|
||||
return fmt.Sprintf(`
|
||||
TX(%x)
|
||||
Contract: %v
|
||||
|
|
@ -508,17 +609,17 @@ func (tx *Transaction) String() string {
|
|||
Hex: %x
|
||||
`,
|
||||
tx.Hash(),
|
||||
tx.data.Recipient == nil,
|
||||
receiver == nil,
|
||||
from,
|
||||
to,
|
||||
tx.data.AccountNonce,
|
||||
tx.data.Price,
|
||||
tx.data.GasLimit,
|
||||
tx.data.Amount,
|
||||
tx.data.Payload,
|
||||
tx.data.V,
|
||||
tx.data.R,
|
||||
tx.data.S,
|
||||
tx.Nonce(),
|
||||
tx.GasPrice(),
|
||||
tx.Gas(),
|
||||
tx.Value(),
|
||||
tx.Data(),
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
enc,
|
||||
)
|
||||
}
|
||||
|
|
@ -562,40 +663,47 @@ func TxDifference(a, b Transactions) (keep Transactions) {
|
|||
type TxByNonce Transactions
|
||||
|
||||
func (s TxByNonce) Len() int { return len(s) }
|
||||
func (s TxByNonce) Less(i, j int) bool { return s[i].data.AccountNonce < s[j].data.AccountNonce }
|
||||
func (s TxByNonce) Less(i, j int) bool { return s[i].Nonce() < s[j].Nonce() }
|
||||
func (s TxByNonce) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
// TxByPrice implements both the sort and the heap interface, making it useful
|
||||
// TxByPriceAndTime implements both the sort and the heap interface, making it useful
|
||||
// for all at once sorting as well as individually adding and removing elements.
|
||||
type TxByPrice struct {
|
||||
type TxByPriceAndTime struct {
|
||||
txs Transactions
|
||||
payersSwap map[common.Address]*big.Int
|
||||
}
|
||||
|
||||
func (s TxByPrice) Len() int { return len(s.txs) }
|
||||
func (s TxByPrice) Less(i, j int) bool {
|
||||
i_price := s.txs[i].data.Price
|
||||
func (s TxByPriceAndTime) Len() int { return len(s.txs) }
|
||||
func (s TxByPriceAndTime) Less(i, j int) bool {
|
||||
i_price := s.txs[i].GasPrice()
|
||||
if s.txs[i].To() != nil {
|
||||
if _, ok := s.payersSwap[*s.txs[i].To()]; ok {
|
||||
i_price = common.TRC21GasPrice
|
||||
}
|
||||
}
|
||||
|
||||
j_price := s.txs[j].data.Price
|
||||
j_price := s.txs[j].GasPrice()
|
||||
if s.txs[j].To() != nil {
|
||||
if _, ok := s.payersSwap[*s.txs[j].To()]; ok {
|
||||
j_price = common.TRC21GasPrice
|
||||
}
|
||||
}
|
||||
return i_price.Cmp(j_price) > 0
|
||||
}
|
||||
func (s TxByPrice) Swap(i, j int) { s.txs[i], s.txs[j] = s.txs[j], s.txs[i] }
|
||||
|
||||
func (s *TxByPrice) Push(x interface{}) {
|
||||
// If the prices are equal, use the time the transaction was first seen for
|
||||
// deterministic sorting
|
||||
cmp := i_price.Cmp(j_price)
|
||||
if cmp == 0 {
|
||||
return s.txs[i].time.Before(s.txs[j].time)
|
||||
}
|
||||
return cmp > 0
|
||||
}
|
||||
func (s TxByPriceAndTime) Swap(i, j int) { s.txs[i], s.txs[j] = s.txs[j], s.txs[i] }
|
||||
|
||||
func (s *TxByPriceAndTime) Push(x interface{}) {
|
||||
s.txs = append(s.txs, x.(*Transaction))
|
||||
}
|
||||
|
||||
func (s *TxByPrice) Pop() interface{} {
|
||||
func (s *TxByPriceAndTime) Pop() interface{} {
|
||||
old := s.txs
|
||||
n := len(old)
|
||||
x := old[n-1]
|
||||
|
|
@ -608,7 +716,7 @@ func (s *TxByPrice) Pop() interface{} {
|
|||
// entire batches of transactions for non-executable accounts.
|
||||
type TransactionsByPriceAndNonce struct {
|
||||
txs map[common.Address]Transactions // Per account nonce-sorted list of transactions
|
||||
heads TxByPrice // Next transaction for each unique account (price heap)
|
||||
heads TxByPriceAndTime // Next transaction for each unique account (price heap)
|
||||
signer Signer // Signer for the set of transactions
|
||||
}
|
||||
|
||||
|
|
@ -617,11 +725,11 @@ type TransactionsByPriceAndNonce struct {
|
|||
//
|
||||
// Note, the input map is reowned so the caller should not interact any more with
|
||||
// if after providing it to the constructor.
|
||||
|
||||
//
|
||||
// It also classifies special txs and normal txs
|
||||
func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions, signers map[common.Address]struct{}, payersSwap map[common.Address]*big.Int) (*TransactionsByPriceAndNonce, Transactions) {
|
||||
// Initialize a price based heap with the head transactions
|
||||
heads := TxByPrice{}
|
||||
// Initialize a price and received time based heap with the head transactions
|
||||
heads := TxByPriceAndTime{}
|
||||
heads.payersSwap = payersSwap
|
||||
specialTxs := Transactions{}
|
||||
for _, accTxs := range txs {
|
||||
|
|
@ -698,11 +806,12 @@ type Message struct {
|
|||
gasLimit uint64
|
||||
gasPrice *big.Int
|
||||
data []byte
|
||||
accessList AccessList
|
||||
checkNonce bool
|
||||
balanceTokenFee *big.Int
|
||||
}
|
||||
|
||||
func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool, balanceTokenFee *big.Int, number *big.Int) Message {
|
||||
func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, accessList AccessList, checkNonce bool, balanceTokenFee *big.Int, number *big.Int) Message {
|
||||
if balanceTokenFee != nil {
|
||||
gasPrice = common.GetGasPrice(number)
|
||||
}
|
||||
|
|
@ -714,6 +823,7 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b
|
|||
gasLimit: gasLimit,
|
||||
gasPrice: gasPrice,
|
||||
data: data,
|
||||
accessList: accessList,
|
||||
checkNonce: checkNonce,
|
||||
balanceTokenFee: balanceTokenFee,
|
||||
}
|
||||
|
|
@ -728,5 +838,6 @@ func (m Message) Gas() uint64 { return m.gasLimit }
|
|||
func (m Message) Nonce() uint64 { return m.nonce }
|
||||
func (m Message) Data() []byte { return m.data }
|
||||
func (m Message) CheckNonce() bool { return m.checkNonce }
|
||||
func (m Message) AccessList() AccessList { return m.accessList }
|
||||
|
||||
func (m *Message) SetNonce(nonce uint64) { m.nonce = nonce }
|
||||
|
|
|
|||
187
core/types/transaction_marshalling.go
Normal file
187
core/types/transaction_marshalling.go
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||
)
|
||||
|
||||
// txJSON is the JSON representation of transactions.
|
||||
type txJSON struct {
|
||||
Type hexutil.Uint64 `json:"type"`
|
||||
|
||||
// Common transaction fields:
|
||||
Nonce *hexutil.Uint64 `json:"nonce"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
Gas *hexutil.Uint64 `json:"gas"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Data *hexutil.Bytes `json:"input"`
|
||||
V *hexutil.Big `json:"v"`
|
||||
R *hexutil.Big `json:"r"`
|
||||
S *hexutil.Big `json:"s"`
|
||||
To *common.Address `json:"to"`
|
||||
|
||||
// Access list transaction fields:
|
||||
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
||||
AccessList *AccessList `json:"accessList,omitempty"`
|
||||
|
||||
// Only used for encoding:
|
||||
Hash common.Hash `json:"hash"`
|
||||
}
|
||||
|
||||
// MarshalJSON marshals as JSON with a hash.
|
||||
func (t *Transaction) MarshalJSON() ([]byte, error) {
|
||||
var enc txJSON
|
||||
// These are set for all tx types.
|
||||
enc.Hash = t.Hash()
|
||||
enc.Type = hexutil.Uint64(t.Type())
|
||||
|
||||
// Other fields are set conditionally depending on tx type.
|
||||
switch tx := t.inner.(type) {
|
||||
case *LegacyTx:
|
||||
enc.Nonce = (*hexutil.Uint64)(&tx.Nonce)
|
||||
enc.Gas = (*hexutil.Uint64)(&tx.Gas)
|
||||
enc.GasPrice = (*hexutil.Big)(tx.GasPrice)
|
||||
enc.Value = (*hexutil.Big)(tx.Value)
|
||||
enc.Data = (*hexutil.Bytes)(&tx.Data)
|
||||
enc.To = t.To()
|
||||
enc.V = (*hexutil.Big)(tx.V)
|
||||
enc.R = (*hexutil.Big)(tx.R)
|
||||
enc.S = (*hexutil.Big)(tx.S)
|
||||
case *AccessListTx:
|
||||
enc.ChainID = (*hexutil.Big)(tx.ChainID)
|
||||
enc.AccessList = &tx.AccessList
|
||||
enc.Nonce = (*hexutil.Uint64)(&tx.Nonce)
|
||||
enc.Gas = (*hexutil.Uint64)(&tx.Gas)
|
||||
enc.GasPrice = (*hexutil.Big)(tx.GasPrice)
|
||||
enc.Value = (*hexutil.Big)(tx.Value)
|
||||
enc.Data = (*hexutil.Bytes)(&tx.Data)
|
||||
enc.To = t.To()
|
||||
enc.V = (*hexutil.Big)(tx.V)
|
||||
enc.R = (*hexutil.Big)(tx.R)
|
||||
enc.S = (*hexutil.Big)(tx.S)
|
||||
}
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (t *Transaction) UnmarshalJSON(input []byte) error {
|
||||
var dec txJSON
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Decode / verify fields according to transaction type.
|
||||
var inner TxData
|
||||
switch dec.Type {
|
||||
case LegacyTxType:
|
||||
var itx LegacyTx
|
||||
inner = &itx
|
||||
if dec.To != nil {
|
||||
itx.To = dec.To
|
||||
}
|
||||
if dec.Nonce == nil {
|
||||
return errors.New("missing required field 'nonce' in transaction")
|
||||
}
|
||||
itx.Nonce = uint64(*dec.Nonce)
|
||||
if dec.GasPrice == nil {
|
||||
return errors.New("missing required field 'gasPrice' in transaction")
|
||||
}
|
||||
itx.GasPrice = (*big.Int)(dec.GasPrice)
|
||||
if dec.Gas == nil {
|
||||
return errors.New("missing required field 'gas' in transaction")
|
||||
}
|
||||
itx.Gas = uint64(*dec.Gas)
|
||||
if dec.Value == nil {
|
||||
return errors.New("missing required field 'value' in transaction")
|
||||
}
|
||||
itx.Value = (*big.Int)(dec.Value)
|
||||
if dec.Data == nil {
|
||||
return errors.New("missing required field 'input' in transaction")
|
||||
}
|
||||
itx.Data = *dec.Data
|
||||
if dec.V == nil {
|
||||
return errors.New("missing required field 'v' in transaction")
|
||||
}
|
||||
itx.V = (*big.Int)(dec.V)
|
||||
if dec.R == nil {
|
||||
return errors.New("missing required field 'r' in transaction")
|
||||
}
|
||||
itx.R = (*big.Int)(dec.R)
|
||||
if dec.S == nil {
|
||||
return errors.New("missing required field 's' in transaction")
|
||||
}
|
||||
itx.S = (*big.Int)(dec.S)
|
||||
withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0
|
||||
if withSignature {
|
||||
if err := sanityCheckSignature(itx.V, itx.R, itx.S, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
case AccessListTxType:
|
||||
var itx AccessListTx
|
||||
inner = &itx
|
||||
// Access list is optional for now.
|
||||
if dec.AccessList != nil {
|
||||
itx.AccessList = *dec.AccessList
|
||||
}
|
||||
if dec.ChainID == nil {
|
||||
return errors.New("missing required field 'chainId' in transaction")
|
||||
}
|
||||
itx.ChainID = (*big.Int)(dec.ChainID)
|
||||
if dec.To != nil {
|
||||
itx.To = dec.To
|
||||
}
|
||||
if dec.Nonce == nil {
|
||||
return errors.New("missing required field 'nonce' in transaction")
|
||||
}
|
||||
itx.Nonce = uint64(*dec.Nonce)
|
||||
if dec.GasPrice == nil {
|
||||
return errors.New("missing required field 'gasPrice' in transaction")
|
||||
}
|
||||
itx.GasPrice = (*big.Int)(dec.GasPrice)
|
||||
if dec.Gas == nil {
|
||||
return errors.New("missing required field 'gas' in transaction")
|
||||
}
|
||||
itx.Gas = uint64(*dec.Gas)
|
||||
if dec.Value == nil {
|
||||
return errors.New("missing required field 'value' in transaction")
|
||||
}
|
||||
itx.Value = (*big.Int)(dec.Value)
|
||||
if dec.Data == nil {
|
||||
return errors.New("missing required field 'input' in transaction")
|
||||
}
|
||||
itx.Data = *dec.Data
|
||||
if dec.V == nil {
|
||||
return errors.New("missing required field 'v' in transaction")
|
||||
}
|
||||
itx.V = (*big.Int)(dec.V)
|
||||
if dec.R == nil {
|
||||
return errors.New("missing required field 'r' in transaction")
|
||||
}
|
||||
itx.R = (*big.Int)(dec.R)
|
||||
if dec.S == nil {
|
||||
return errors.New("missing required field 's' in transaction")
|
||||
}
|
||||
itx.S = (*big.Int)(dec.S)
|
||||
withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0
|
||||
if withSignature {
|
||||
if err := sanityCheckSignature(itx.V, itx.R, itx.S, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return ErrTxTypeNotSupported
|
||||
}
|
||||
|
||||
// Now set the inner transaction.
|
||||
t.setDecoded(inner, 0)
|
||||
|
||||
// TODO: check hash here?
|
||||
return nil
|
||||
}
|
||||
|
|
@ -27,9 +27,8 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidChainId = errors.New("invalid chain id for signer")
|
||||
)
|
||||
var ErrInvalidChainId = errors.New("invalid chain id for signer")
|
||||
var ErrInvalidNilTx = errors.New("invalid nil tx")
|
||||
|
||||
// sigCache is used to cache the derived sender and contains
|
||||
// the signer used to derive it.
|
||||
|
|
@ -42,6 +41,8 @@ type sigCache struct {
|
|||
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
|
||||
var signer Signer
|
||||
switch {
|
||||
case config.IsEIP1559(blockNumber):
|
||||
signer = NewEIP2930Signer(config.ChainId)
|
||||
case config.IsEIP155(blockNumber):
|
||||
signer = NewEIP155Signer(config.ChainId)
|
||||
case config.IsHomestead(blockNumber):
|
||||
|
|
@ -52,7 +53,40 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
|
|||
return signer
|
||||
}
|
||||
|
||||
// SignTx signs the transaction using the given signer and private key
|
||||
// LatestSigner returns the 'most permissive' Signer available for the given chain
|
||||
// configuration. Specifically, this enables support of EIP-155 replay protection and
|
||||
// EIP-2930 access list transactions when their respective forks are scheduled to occur at
|
||||
// any block number in the chain config.
|
||||
//
|
||||
// Use this in transaction-handling code where the current block number is unknown. If you
|
||||
// have the current block number available, use MakeSigner instead.
|
||||
func LatestSigner(config *params.ChainConfig) Signer {
|
||||
if config.ChainId != nil {
|
||||
if common.Eip1559Block.Uint64() != 9999999999 || config.Eip1559Block != nil {
|
||||
return NewEIP2930Signer(config.ChainId)
|
||||
}
|
||||
if config.EIP155Block != nil {
|
||||
return NewEIP155Signer(config.ChainId)
|
||||
}
|
||||
}
|
||||
return HomesteadSigner{}
|
||||
}
|
||||
|
||||
// LatestSignerForChainID returns the 'most permissive' Signer available. Specifically,
|
||||
// this enables support for EIP-155 replay protection and all implemented EIP-2718
|
||||
// transaction types if chainID is non-nil.
|
||||
//
|
||||
// Use this in transaction-handling code where the current block number and fork
|
||||
// configuration are unknown. If you have a ChainConfig, use LatestSigner instead.
|
||||
// If you have a ChainConfig and know the current block number, use MakeSigner instead.
|
||||
func LatestSignerForChainID(chainID *big.Int) Signer {
|
||||
if chainID == nil {
|
||||
return HomesteadSigner{}
|
||||
}
|
||||
return NewEIP2930Signer(chainID)
|
||||
}
|
||||
|
||||
// SignTx signs the transaction using the given signer and private key.
|
||||
func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, error) {
|
||||
h := s.Hash(tx)
|
||||
sig, err := crypto.Sign(h[:], prv)
|
||||
|
|
@ -62,6 +96,27 @@ func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, err
|
|||
return tx.WithSignature(s, sig)
|
||||
}
|
||||
|
||||
// SignNewTx creates a transaction and signs it.
|
||||
func SignNewTx(prv *ecdsa.PrivateKey, s Signer, txdata TxData) (*Transaction, error) {
|
||||
tx := NewTx(txdata)
|
||||
h := s.Hash(tx)
|
||||
sig, err := crypto.Sign(h[:], prv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tx.WithSignature(s, sig)
|
||||
}
|
||||
|
||||
// MustSignNewTx creates a transaction and signs it.
|
||||
// This panics if the transaction cannot be signed.
|
||||
func MustSignNewTx(prv *ecdsa.PrivateKey, s Signer, txdata TxData) *Transaction {
|
||||
tx, err := SignNewTx(prv, s, txdata)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
// Sender returns the address derived from the signature (V, R, S) using secp256k1
|
||||
// elliptic curve and an error if it failed deriving or upon an incorrect
|
||||
// signature.
|
||||
|
|
@ -70,6 +125,10 @@ func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, err
|
|||
// signing method. The cache is invalidated if the cached signer does
|
||||
// not match the signer used in the current call.
|
||||
func Sender(signer Signer, tx *Transaction) (common.Address, error) {
|
||||
if tx == nil {
|
||||
return common.Address{}, ErrInvalidNilTx
|
||||
}
|
||||
|
||||
if sc := tx.from.Load(); sc != nil {
|
||||
sigCache := sc.(sigCache)
|
||||
// If the signer used to derive from in a previous
|
||||
|
|
@ -88,21 +147,112 @@ func Sender(signer Signer, tx *Transaction) (common.Address, error) {
|
|||
return addr, nil
|
||||
}
|
||||
|
||||
// Signer encapsulates transaction signature handling. Note that this interface is not a
|
||||
// stable API and may change at any time to accommodate new protocol rules.
|
||||
// Signer encapsulates transaction signature handling. The name of this type is slightly
|
||||
// misleading because Signers don't actually sign, they're just for validating and
|
||||
// processing of signatures.
|
||||
//
|
||||
// Note that this interface is not a stable API and may change at any time to accommodate
|
||||
// new protocol rules.
|
||||
type Signer interface {
|
||||
// Sender returns the sender address of the transaction.
|
||||
Sender(tx *Transaction) (common.Address, error)
|
||||
|
||||
// SignatureValues returns the raw R, S, V values corresponding to the
|
||||
// given signature.
|
||||
SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error)
|
||||
// Hash returns the hash to be signed.
|
||||
ChainID() *big.Int
|
||||
|
||||
// Hash returns 'signature hash', i.e. the transaction hash that is signed by the
|
||||
// private key. This hash does not uniquely identify the transaction.
|
||||
Hash(tx *Transaction) common.Hash
|
||||
|
||||
// Equal returns true if the given signer is the same as the receiver.
|
||||
Equal(Signer) bool
|
||||
}
|
||||
|
||||
// EIP155Transaction implements Signer using the EIP155 rules.
|
||||
type eip2930Signer struct{ EIP155Signer }
|
||||
|
||||
// NewEIP2930Signer returns a signer that accepts EIP-2930 access list transactions,
|
||||
// EIP-155 replay protected transactions, and legacy Homestead transactions.
|
||||
func NewEIP2930Signer(chainId *big.Int) Signer {
|
||||
return eip2930Signer{NewEIP155Signer(chainId)}
|
||||
}
|
||||
|
||||
func (s eip2930Signer) ChainID() *big.Int {
|
||||
return s.chainId
|
||||
}
|
||||
|
||||
func (s eip2930Signer) Equal(s2 Signer) bool {
|
||||
x, ok := s2.(eip2930Signer)
|
||||
return ok && x.chainId.Cmp(s.chainId) == 0
|
||||
}
|
||||
|
||||
func (s eip2930Signer) Sender(tx *Transaction) (common.Address, error) {
|
||||
V, R, S := tx.RawSignatureValues()
|
||||
switch tx.Type() {
|
||||
case LegacyTxType:
|
||||
return s.EIP155Signer.Sender(tx)
|
||||
case AccessListTxType:
|
||||
// ACL txs are defined to use 0 and 1 as their recovery id, add
|
||||
// 27 to become equivalent to unprotected Homestead signatures.
|
||||
V = new(big.Int).Add(V, big.NewInt(27))
|
||||
default:
|
||||
return common.Address{}, ErrTxTypeNotSupported
|
||||
}
|
||||
if tx.ChainId().Cmp(s.chainId) != 0 {
|
||||
return common.Address{}, ErrInvalidChainId
|
||||
}
|
||||
return recoverPlain(s.Hash(tx), R, S, V, true)
|
||||
}
|
||||
|
||||
func (s eip2930Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
|
||||
switch txdata := tx.inner.(type) {
|
||||
case *LegacyTx:
|
||||
return s.EIP155Signer.SignatureValues(tx, sig)
|
||||
case *AccessListTx:
|
||||
// Check that chain ID of tx matches the signer. We also accept ID zero here,
|
||||
// because it indicates that the chain ID was not specified in the tx.
|
||||
if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(s.chainId) != 0 {
|
||||
return nil, nil, nil, ErrInvalidChainId
|
||||
}
|
||||
R, S, _ = decodeSignature(sig)
|
||||
V = big.NewInt(int64(sig[64]))
|
||||
default:
|
||||
return nil, nil, nil, ErrTxTypeNotSupported
|
||||
}
|
||||
return R, S, V, nil
|
||||
}
|
||||
|
||||
// Hash returns the hash to be signed by the sender.
|
||||
// It does not uniquely identify the transaction.
|
||||
func (s eip2930Signer) Hash(tx *Transaction) common.Hash {
|
||||
switch tx.Type() {
|
||||
case LegacyTxType:
|
||||
return s.EIP155Signer.Hash(tx)
|
||||
case AccessListTxType:
|
||||
return prefixedRlpHash(
|
||||
tx.Type(),
|
||||
[]interface{}{
|
||||
s.chainId,
|
||||
tx.Nonce(),
|
||||
tx.GasPrice(),
|
||||
tx.Gas(),
|
||||
tx.To(),
|
||||
tx.Value(),
|
||||
tx.Data(),
|
||||
tx.AccessList(),
|
||||
})
|
||||
default:
|
||||
// This _should_ not happen, but in case someone sends in a bad
|
||||
// json struct via RPC, it's probably more prudent to return an
|
||||
// empty hash instead of killing the node with a panic
|
||||
//panic("Unsupported transaction type: %d", tx.typ)
|
||||
return common.Hash{}
|
||||
}
|
||||
}
|
||||
|
||||
// EIP155Signer implements Signer using the EIP-155 rules. This accepts transactions which
|
||||
// are replay-protected as well as unprotected homestead transactions.
|
||||
type EIP155Signer struct {
|
||||
chainId, chainIdMul *big.Int
|
||||
}
|
||||
|
|
@ -117,6 +267,10 @@ func NewEIP155Signer(chainId *big.Int) EIP155Signer {
|
|||
}
|
||||
}
|
||||
|
||||
func (s EIP155Signer) ChainID() *big.Int {
|
||||
return s.chainId
|
||||
}
|
||||
|
||||
func (s EIP155Signer) Equal(s2 Signer) bool {
|
||||
eip155, ok := s2.(EIP155Signer)
|
||||
return ok && eip155.chainId.Cmp(s.chainId) == 0
|
||||
|
|
@ -125,24 +279,28 @@ func (s EIP155Signer) Equal(s2 Signer) bool {
|
|||
var big8 = big.NewInt(8)
|
||||
|
||||
func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) {
|
||||
if tx.Type() != LegacyTxType {
|
||||
return common.Address{}, ErrTxTypeNotSupported
|
||||
}
|
||||
if !tx.Protected() {
|
||||
return HomesteadSigner{}.Sender(tx)
|
||||
}
|
||||
if tx.ChainId().Cmp(s.chainId) != 0 {
|
||||
return common.Address{}, ErrInvalidChainId
|
||||
}
|
||||
V := new(big.Int).Sub(tx.data.V, s.chainIdMul)
|
||||
V, R, S := tx.RawSignatureValues()
|
||||
V = new(big.Int).Sub(V, s.chainIdMul)
|
||||
V.Sub(V, big8)
|
||||
return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true)
|
||||
return recoverPlain(s.Hash(tx), R, S, V, true)
|
||||
}
|
||||
|
||||
// WithSignature returns a new transaction with the given signature. This signature
|
||||
// SignatureValues returns signature values. This signature
|
||||
// needs to be in the [R || S || V] format where V is 0 or 1.
|
||||
func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
|
||||
R, S, V, err = HomesteadSigner{}.SignatureValues(tx, sig)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
if tx.Type() != LegacyTxType {
|
||||
return nil, nil, nil, ErrTxTypeNotSupported
|
||||
}
|
||||
R, S, V = decodeSignature(sig)
|
||||
if s.chainId.Sign() != 0 {
|
||||
V = big.NewInt(int64(sig[64] + 35))
|
||||
V.Add(V, s.chainIdMul)
|
||||
|
|
@ -154,12 +312,12 @@ func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big
|
|||
// It does not uniquely identify the transaction.
|
||||
func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
|
||||
return rlpHash([]interface{}{
|
||||
tx.data.AccountNonce,
|
||||
tx.data.Price,
|
||||
tx.data.GasLimit,
|
||||
tx.data.Recipient,
|
||||
tx.data.Amount,
|
||||
tx.data.Payload,
|
||||
tx.Nonce(),
|
||||
tx.GasPrice(),
|
||||
tx.Gas(),
|
||||
tx.To(),
|
||||
tx.Value(),
|
||||
tx.Data(),
|
||||
s.chainId, uint(0), uint(0),
|
||||
})
|
||||
}
|
||||
|
|
@ -168,6 +326,10 @@ func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
|
|||
// homestead rules.
|
||||
type HomesteadSigner struct{ FrontierSigner }
|
||||
|
||||
func (s HomesteadSigner) ChainID() *big.Int {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s HomesteadSigner) Equal(s2 Signer) bool {
|
||||
_, ok := s2.(HomesteadSigner)
|
||||
return ok
|
||||
|
|
@ -180,25 +342,39 @@ func (hs HomesteadSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v
|
|||
}
|
||||
|
||||
func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) {
|
||||
return recoverPlain(hs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, true)
|
||||
if tx.Type() != LegacyTxType {
|
||||
return common.Address{}, ErrTxTypeNotSupported
|
||||
}
|
||||
v, r, s := tx.RawSignatureValues()
|
||||
return recoverPlain(hs.Hash(tx), r, s, v, true)
|
||||
}
|
||||
|
||||
type FrontierSigner struct{}
|
||||
|
||||
func (s FrontierSigner) ChainID() *big.Int {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s FrontierSigner) Equal(s2 Signer) bool {
|
||||
_, ok := s2.(FrontierSigner)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (fs FrontierSigner) Sender(tx *Transaction) (common.Address, error) {
|
||||
if tx.Type() != LegacyTxType {
|
||||
return common.Address{}, ErrTxTypeNotSupported
|
||||
}
|
||||
v, r, s := tx.RawSignatureValues()
|
||||
return recoverPlain(fs.Hash(tx), r, s, v, false)
|
||||
}
|
||||
|
||||
// SignatureValues returns signature values. This signature
|
||||
// needs to be in the [R || S || V] format where V is 0 or 1.
|
||||
func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
|
||||
if len(sig) != 65 {
|
||||
panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig)))
|
||||
if tx.Type() != LegacyTxType {
|
||||
return nil, nil, nil, ErrTxTypeNotSupported
|
||||
}
|
||||
r = new(big.Int).SetBytes(sig[:32])
|
||||
s = new(big.Int).SetBytes(sig[32:64])
|
||||
v = new(big.Int).SetBytes([]byte{sig[64] + 27})
|
||||
r, s, v = decodeSignature(sig)
|
||||
return r, s, v, nil
|
||||
}
|
||||
|
||||
|
|
@ -206,17 +382,23 @@ func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *
|
|||
// It does not uniquely identify the transaction.
|
||||
func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
|
||||
return rlpHash([]interface{}{
|
||||
tx.data.AccountNonce,
|
||||
tx.data.Price,
|
||||
tx.data.GasLimit,
|
||||
tx.data.Recipient,
|
||||
tx.data.Amount,
|
||||
tx.data.Payload,
|
||||
tx.Nonce(),
|
||||
tx.GasPrice(),
|
||||
tx.Gas(),
|
||||
tx.To(),
|
||||
tx.Value(),
|
||||
tx.Data(),
|
||||
})
|
||||
}
|
||||
|
||||
func (fs FrontierSigner) Sender(tx *Transaction) (common.Address, error) {
|
||||
return recoverPlain(fs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, false)
|
||||
func decodeSignature(sig []byte) (r, s, v *big.Int) {
|
||||
if len(sig) != crypto.SignatureLength {
|
||||
panic(fmt.Sprintf("wrong size for signature: got %d, want %d", len(sig), crypto.SignatureLength))
|
||||
}
|
||||
r = new(big.Int).SetBytes(sig[:32])
|
||||
s = new(big.Int).SetBytes(sig[32:64])
|
||||
v = new(big.Int).SetBytes([]byte{sig[64] + 27})
|
||||
return r, s, v
|
||||
}
|
||||
|
||||
func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (common.Address, error) {
|
||||
|
|
|
|||
|
|
@ -20,8 +20,12 @@ import (
|
|||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
|
|
@ -31,6 +35,8 @@ import (
|
|||
// The values in those tests are from the Transaction Tests
|
||||
// at github.com/ethereum/tests.
|
||||
var (
|
||||
testAddr = common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b")
|
||||
|
||||
emptyTx = NewTransaction(
|
||||
0,
|
||||
common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
|
||||
|
|
@ -40,7 +46,7 @@ var (
|
|||
|
||||
rightvrsTx, _ = NewTransaction(
|
||||
3,
|
||||
common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"),
|
||||
testAddr,
|
||||
big.NewInt(10),
|
||||
2000,
|
||||
big.NewInt(1),
|
||||
|
|
@ -49,8 +55,32 @@ var (
|
|||
HomesteadSigner{},
|
||||
common.Hex2Bytes("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a301"),
|
||||
)
|
||||
|
||||
emptyEip2718Tx = NewTx(&AccessListTx{
|
||||
ChainID: big.NewInt(1),
|
||||
Nonce: 3,
|
||||
To: &testAddr,
|
||||
Value: big.NewInt(10),
|
||||
Gas: 25000,
|
||||
GasPrice: big.NewInt(1),
|
||||
Data: common.FromHex("5544"),
|
||||
})
|
||||
|
||||
signedEip2718Tx, _ = emptyEip2718Tx.WithSignature(
|
||||
NewEIP2930Signer(big.NewInt(1)),
|
||||
common.Hex2Bytes("c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b266032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d3752101"),
|
||||
)
|
||||
)
|
||||
|
||||
func TestDecodeEmptyTypedTx(t *testing.T) {
|
||||
input := []byte{0x80}
|
||||
var tx Transaction
|
||||
err := rlp.DecodeBytes(input, &tx)
|
||||
if err != errEmptyTypedTx {
|
||||
t.Fatal("wrong error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransactionSigHash(t *testing.T) {
|
||||
var homestead HomesteadSigner
|
||||
if homestead.Hash(emptyTx) != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") {
|
||||
|
|
@ -72,6 +102,117 @@ func TestTransactionEncode(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestEIP2718TransactionSigHash(t *testing.T) {
|
||||
s := NewEIP2930Signer(big.NewInt(1))
|
||||
if s.Hash(emptyEip2718Tx) != common.HexToHash("49b486f0ec0a60dfbbca2d30cb07c9e8ffb2a2ff41f29a1ab6737475f6ff69f3") {
|
||||
t.Errorf("empty EIP-2718 transaction hash mismatch, got %x", s.Hash(emptyEip2718Tx))
|
||||
}
|
||||
if s.Hash(signedEip2718Tx) != common.HexToHash("49b486f0ec0a60dfbbca2d30cb07c9e8ffb2a2ff41f29a1ab6737475f6ff69f3") {
|
||||
t.Errorf("signed EIP-2718 transaction hash mismatch, got %x", s.Hash(signedEip2718Tx))
|
||||
}
|
||||
}
|
||||
|
||||
// This test checks signature operations on access list transactions.
|
||||
func TestEIP2930Signer(t *testing.T) {
|
||||
var (
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
keyAddr = crypto.PubkeyToAddress(key.PublicKey)
|
||||
signer1 = NewEIP2930Signer(big.NewInt(1))
|
||||
signer2 = NewEIP2930Signer(big.NewInt(2))
|
||||
tx0 = NewTx(&AccessListTx{Nonce: 1})
|
||||
tx1 = NewTx(&AccessListTx{ChainID: big.NewInt(1), Nonce: 1})
|
||||
tx2, _ = SignNewTx(key, signer2, &AccessListTx{ChainID: big.NewInt(2), Nonce: 1})
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
tx *Transaction
|
||||
signer Signer
|
||||
wantSignerHash common.Hash
|
||||
wantSenderErr error
|
||||
wantSignErr error
|
||||
wantHash common.Hash // after signing
|
||||
}{
|
||||
{
|
||||
tx: tx0,
|
||||
signer: signer1,
|
||||
wantSignerHash: common.HexToHash("846ad7672f2a3a40c1f959cd4a8ad21786d620077084d84c8d7c077714caa139"),
|
||||
wantSenderErr: ErrInvalidChainId,
|
||||
wantHash: common.HexToHash("1ccd12d8bbdb96ea391af49a35ab641e219b2dd638dea375f2bc94dd290f2549"),
|
||||
},
|
||||
{
|
||||
tx: tx1,
|
||||
signer: signer1,
|
||||
wantSenderErr: ErrInvalidSig,
|
||||
wantSignerHash: common.HexToHash("846ad7672f2a3a40c1f959cd4a8ad21786d620077084d84c8d7c077714caa139"),
|
||||
wantHash: common.HexToHash("1ccd12d8bbdb96ea391af49a35ab641e219b2dd638dea375f2bc94dd290f2549"),
|
||||
},
|
||||
{
|
||||
// This checks what happens when trying to sign an unsigned tx for the wrong chain.
|
||||
tx: tx1,
|
||||
signer: signer2,
|
||||
wantSenderErr: ErrInvalidChainId,
|
||||
wantSignerHash: common.HexToHash("367967247499343401261d718ed5aa4c9486583e4d89251afce47f4a33c33362"),
|
||||
wantSignErr: ErrInvalidChainId,
|
||||
},
|
||||
{
|
||||
// This checks what happens when trying to re-sign a signed tx for the wrong chain.
|
||||
tx: tx2,
|
||||
signer: signer1,
|
||||
wantSenderErr: ErrInvalidChainId,
|
||||
wantSignerHash: common.HexToHash("846ad7672f2a3a40c1f959cd4a8ad21786d620077084d84c8d7c077714caa139"),
|
||||
wantSignErr: ErrInvalidChainId,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
sigHash := test.signer.Hash(test.tx)
|
||||
if sigHash != test.wantSignerHash {
|
||||
t.Errorf("test %d: wrong sig hash: got %x, want %x", i, sigHash, test.wantSignerHash)
|
||||
}
|
||||
sender, err := Sender(test.signer, test.tx)
|
||||
if !errors.Is(err, test.wantSenderErr) {
|
||||
t.Errorf("test %d: wrong Sender error %q", i, err)
|
||||
}
|
||||
if err == nil && sender != keyAddr {
|
||||
t.Errorf("test %d: wrong sender address %x", i, sender)
|
||||
}
|
||||
signedTx, err := SignTx(test.tx, test.signer, key)
|
||||
if !errors.Is(err, test.wantSignErr) {
|
||||
t.Fatalf("test %d: wrong SignTx error %q", i, err)
|
||||
}
|
||||
if signedTx != nil {
|
||||
if signedTx.Hash() != test.wantHash {
|
||||
t.Errorf("test %d: wrong tx hash after signing: got %x, want %x", i, signedTx.Hash(), test.wantHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEIP2718TransactionEncode(t *testing.T) {
|
||||
// RLP representation
|
||||
{
|
||||
have, err := rlp.EncodeToBytes(signedEip2718Tx)
|
||||
if err != nil {
|
||||
t.Fatalf("encode error: %v", err)
|
||||
}
|
||||
want := common.FromHex("b86601f8630103018261a894b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a825544c001a0c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b2660a032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d37521")
|
||||
if !bytes.Equal(have, want) {
|
||||
t.Errorf("encoded RLP mismatch, got %x", have)
|
||||
}
|
||||
}
|
||||
// Binary representation
|
||||
{
|
||||
have, err := signedEip2718Tx.MarshalBinary()
|
||||
if err != nil {
|
||||
t.Fatalf("encode error: %v", err)
|
||||
}
|
||||
want := common.FromHex("01f8630103018261a894b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a825544c001a0c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b2660a032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d37521")
|
||||
if !bytes.Equal(have, want) {
|
||||
t.Errorf("encoded RLP mismatch, got %x", have)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeTx(data []byte) (*Transaction, error) {
|
||||
var tx Transaction
|
||||
t, err := &tx, rlp.Decode(bytes.NewReader(data), &tx)
|
||||
|
|
@ -233,3 +374,174 @@ func TestTransactionJSON(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that if multiple transactions have the same price, the ones seen earlier
|
||||
// are prioritized to avoid network spam attacks aiming for a specific ordering.
|
||||
func TestTransactionTimeSort(t *testing.T) {
|
||||
// Generate a batch of accounts to start with
|
||||
keys := make([]*ecdsa.PrivateKey, 5)
|
||||
for i := 0; i < len(keys); i++ {
|
||||
keys[i], _ = crypto.GenerateKey()
|
||||
}
|
||||
signer := HomesteadSigner{}
|
||||
|
||||
// Generate a batch of transactions with overlapping prices, but different creation times
|
||||
groups := map[common.Address]Transactions{}
|
||||
for start, key := range keys {
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
|
||||
tx, _ := SignTx(NewTransaction(0, common.Address{}, big.NewInt(100), 100, big.NewInt(1), nil), signer, key)
|
||||
tx.time = time.Unix(0, int64(len(keys)-start))
|
||||
|
||||
groups[addr] = append(groups[addr], tx)
|
||||
}
|
||||
// Sort the transactions and cross check the nonce ordering
|
||||
txset, _ := NewTransactionsByPriceAndNonce(signer, groups, nil, map[common.Address]*big.Int{})
|
||||
|
||||
txs := Transactions{}
|
||||
for tx := txset.Peek(); tx != nil; tx = txset.Peek() {
|
||||
txs = append(txs, tx)
|
||||
txset.Shift()
|
||||
}
|
||||
if len(txs) != len(keys) {
|
||||
t.Errorf("expected %d transactions, found %d", len(keys), len(txs))
|
||||
}
|
||||
for i, txi := range txs {
|
||||
fromi, _ := Sender(signer, txi)
|
||||
if i+1 < len(txs) {
|
||||
next := txs[i+1]
|
||||
fromNext, _ := Sender(signer, next)
|
||||
|
||||
if txi.GasPrice().Cmp(next.GasPrice()) < 0 {
|
||||
t.Errorf("invalid gasprice ordering: tx #%d (A=%x P=%v) < tx #%d (A=%x P=%v)", i, fromi[:4], txi.GasPrice(), i+1, fromNext[:4], next.GasPrice())
|
||||
}
|
||||
// Make sure time order is ascending if the txs have the same gas price
|
||||
if txi.GasPrice().Cmp(next.GasPrice()) == 0 && txi.time.After(next.time) {
|
||||
t.Errorf("invalid received time ordering: tx #%d (A=%x T=%v) > tx #%d (A=%x T=%v)", i, fromi[:4], txi.time, i+1, fromNext[:4], next.time)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestTransactionCoding tests serializing/de-serializing to/from rlp and JSON.
|
||||
func TestTransactionCoding(t *testing.T) {
|
||||
key, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("could not generate key: %v", err)
|
||||
}
|
||||
var (
|
||||
signer = NewEIP2930Signer(common.Big1)
|
||||
addr = common.HexToAddress("0x0000000000000000000000000000000000000001")
|
||||
recipient = common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87")
|
||||
accesses = AccessList{{Address: addr, StorageKeys: []common.Hash{{0}}}}
|
||||
)
|
||||
for i := uint64(0); i < 500; i++ {
|
||||
var txdata TxData
|
||||
switch i % 5 {
|
||||
case 0:
|
||||
// Legacy tx.
|
||||
txdata = &LegacyTx{
|
||||
Nonce: i,
|
||||
To: &recipient,
|
||||
Gas: 1,
|
||||
GasPrice: big.NewInt(2),
|
||||
Data: []byte("abcdef"),
|
||||
}
|
||||
case 1:
|
||||
// Legacy tx contract creation.
|
||||
txdata = &LegacyTx{
|
||||
Nonce: i,
|
||||
Gas: 1,
|
||||
GasPrice: big.NewInt(2),
|
||||
Data: []byte("abcdef"),
|
||||
}
|
||||
case 2:
|
||||
// Tx with non-zero access list.
|
||||
txdata = &AccessListTx{
|
||||
ChainID: big.NewInt(1),
|
||||
Nonce: i,
|
||||
To: &recipient,
|
||||
Gas: 123457,
|
||||
GasPrice: big.NewInt(10),
|
||||
AccessList: accesses,
|
||||
Data: []byte("abcdef"),
|
||||
}
|
||||
case 3:
|
||||
// Tx with empty access list.
|
||||
txdata = &AccessListTx{
|
||||
ChainID: big.NewInt(1),
|
||||
Nonce: i,
|
||||
To: &recipient,
|
||||
Gas: 123457,
|
||||
GasPrice: big.NewInt(10),
|
||||
Data: []byte("abcdef"),
|
||||
}
|
||||
case 4:
|
||||
// Contract creation with access list.
|
||||
txdata = &AccessListTx{
|
||||
ChainID: big.NewInt(1),
|
||||
Nonce: i,
|
||||
Gas: 123457,
|
||||
GasPrice: big.NewInt(10),
|
||||
AccessList: accesses,
|
||||
}
|
||||
}
|
||||
tx, err := SignNewTx(key, signer, txdata)
|
||||
if err != nil {
|
||||
t.Fatalf("could not sign transaction: %v", err)
|
||||
}
|
||||
// RLP
|
||||
parsedTx, err := encodeDecodeBinary(tx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertEqual(parsedTx, tx)
|
||||
|
||||
// JSON
|
||||
parsedTx, err = encodeDecodeJSON(tx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertEqual(parsedTx, tx)
|
||||
}
|
||||
}
|
||||
|
||||
func encodeDecodeJSON(tx *Transaction) (*Transaction, error) {
|
||||
data, err := json.Marshal(tx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("json encoding failed: %v", err)
|
||||
}
|
||||
var parsedTx = &Transaction{}
|
||||
if err := json.Unmarshal(data, &parsedTx); err != nil {
|
||||
return nil, fmt.Errorf("json decoding failed: %v", err)
|
||||
}
|
||||
return parsedTx, nil
|
||||
}
|
||||
|
||||
func encodeDecodeBinary(tx *Transaction) (*Transaction, error) {
|
||||
data, err := tx.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("rlp encoding failed: %v", err)
|
||||
}
|
||||
var parsedTx = &Transaction{}
|
||||
if err := parsedTx.UnmarshalBinary(data); err != nil {
|
||||
return nil, fmt.Errorf("rlp decoding failed: %v", err)
|
||||
}
|
||||
return parsedTx, nil
|
||||
}
|
||||
|
||||
func assertEqual(orig *Transaction, cpy *Transaction) error {
|
||||
// compare nonce, price, gaslimit, recipient, amount, payload, V, R, S
|
||||
if want, got := orig.Hash(), cpy.Hash(); want != got {
|
||||
return fmt.Errorf("parsed tx differs from original tx, want %v, got %v", want, got)
|
||||
}
|
||||
if want, got := orig.ChainId(), cpy.ChainId(); want.Cmp(got) != 0 {
|
||||
return fmt.Errorf("invalid chain id, want %d, got %d", want, got)
|
||||
}
|
||||
if orig.AccessList() != nil {
|
||||
if !reflect.DeepEqual(orig.AccessList(), cpy.AccessList()) {
|
||||
return fmt.Errorf("access list wrong!")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
177
core/vm/access_list_tracer.go
Normal file
177
core/vm/access_list_tracer.go
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
// Copyright 2021 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"
|
||||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
)
|
||||
|
||||
// accessList is an accumulator for the set of accounts and storage slots an EVM
|
||||
// contract execution touches.
|
||||
type accessList map[common.Address]accessListSlots
|
||||
|
||||
// accessListSlots is an accumulator for the set of storage slots within a single
|
||||
// contract that an EVM contract execution touches.
|
||||
type accessListSlots map[common.Hash]struct{}
|
||||
|
||||
// newAccessList creates a new accessList.
|
||||
func newAccessList() accessList {
|
||||
return make(map[common.Address]accessListSlots)
|
||||
}
|
||||
|
||||
// addAddress adds an address to the accesslist.
|
||||
func (al accessList) addAddress(address common.Address) {
|
||||
// Set address if not previously present
|
||||
if _, present := al[address]; !present {
|
||||
al[address] = make(map[common.Hash]struct{})
|
||||
}
|
||||
}
|
||||
|
||||
// addSlot adds a storage slot to the accesslist.
|
||||
func (al accessList) addSlot(address common.Address, slot common.Hash) {
|
||||
// Set address if not previously present
|
||||
al.addAddress(address)
|
||||
|
||||
// Set the slot on the surely existent storage set
|
||||
al[address][slot] = struct{}{}
|
||||
}
|
||||
|
||||
// equal checks if the content of the current access list is the same as the
|
||||
// content of the other one.
|
||||
func (al accessList) equal(other accessList) bool {
|
||||
// Cross reference the accounts first
|
||||
if len(al) != len(other) {
|
||||
return false
|
||||
}
|
||||
for addr := range al {
|
||||
if _, ok := other[addr]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for addr := range other {
|
||||
if _, ok := al[addr]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Accounts match, cross reference the storage slots too
|
||||
for addr, slots := range al {
|
||||
otherslots := other[addr]
|
||||
|
||||
if len(slots) != len(otherslots) {
|
||||
return false
|
||||
}
|
||||
for hash := range slots {
|
||||
if _, ok := otherslots[hash]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for hash := range otherslots {
|
||||
if _, ok := slots[hash]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// accesslist converts the accesslist to a types.AccessList.
|
||||
func (al accessList) accessList() types.AccessList {
|
||||
acl := make(types.AccessList, 0, len(al))
|
||||
for addr, slots := range al {
|
||||
tuple := types.AccessTuple{Address: addr}
|
||||
for slot := range slots {
|
||||
tuple.StorageKeys = append(tuple.StorageKeys, slot)
|
||||
}
|
||||
acl = append(acl, tuple)
|
||||
}
|
||||
return acl
|
||||
}
|
||||
|
||||
// AccessListTracer is a tracer that accumulates touched accounts and storage
|
||||
// slots into an internal set.
|
||||
type AccessListTracer struct {
|
||||
excl map[common.Address]struct{} // Set of account to exclude from the list
|
||||
list accessList // Set of accounts and storage slots touched
|
||||
}
|
||||
|
||||
// NewAccessListTracer creates a new tracer that can generate AccessLists.
|
||||
// An optional AccessList can be specified to occupy slots and addresses in
|
||||
// the resulting accesslist.
|
||||
func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompiles []common.Address) *AccessListTracer {
|
||||
excl := map[common.Address]struct{}{
|
||||
from: {}, to: {},
|
||||
}
|
||||
for _, addr := range precompiles {
|
||||
excl[addr] = struct{}{}
|
||||
}
|
||||
list := newAccessList()
|
||||
for _, al := range acl {
|
||||
if _, ok := excl[al.Address]; !ok {
|
||||
list.addAddress(al.Address)
|
||||
}
|
||||
for _, slot := range al.StorageKeys {
|
||||
list.addSlot(al.Address, slot)
|
||||
}
|
||||
}
|
||||
return &AccessListTracer{
|
||||
excl: excl,
|
||||
list: list,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AccessListTracer) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||
}
|
||||
|
||||
// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
|
||||
func (a *AccessListTracer) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
|
||||
stack := scope.Stack
|
||||
if (op == SLOAD || op == SSTORE) && stack.len() >= 1 {
|
||||
slot := common.Hash(stack.data[stack.len()-1].Bytes32())
|
||||
a.list.addSlot(scope.Contract.Address(), slot)
|
||||
}
|
||||
if (op == EXTCODECOPY || op == EXTCODEHASH || op == EXTCODESIZE || op == BALANCE || op == SELFDESTRUCT) && stack.len() >= 1 {
|
||||
addr := common.Address(stack.data[stack.len()-1].Bytes20())
|
||||
if _, ok := a.excl[addr]; !ok {
|
||||
a.list.addAddress(addr)
|
||||
}
|
||||
}
|
||||
if (op == DELEGATECALL || op == CALL || op == STATICCALL || op == CALLCODE) && stack.len() >= 5 {
|
||||
addr := common.Address(stack.data[stack.len()-2].Bytes20())
|
||||
if _, ok := a.excl[addr]; !ok {
|
||||
a.list.addAddress(addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (*AccessListTracer) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
|
||||
}
|
||||
|
||||
func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {}
|
||||
|
||||
// AccessList returns the current accesslist maintained by the tracer.
|
||||
func (a *AccessListTracer) AccessList() types.AccessList {
|
||||
return a.list.accessList()
|
||||
}
|
||||
|
||||
// Equal returns if the content of two access list traces are equal.
|
||||
func (a *AccessListTracer) Equal(other *AccessListTracer) bool {
|
||||
return a.list.equal(other.list)
|
||||
}
|
||||
|
|
@ -22,10 +22,9 @@ import (
|
|||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm/privacy"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm/privacy"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto/blake2b"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto/bn256"
|
||||
|
|
@ -87,6 +86,54 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{
|
|||
common.BytesToAddress([]byte{42}): &XDCxEpochPrice{},
|
||||
}
|
||||
|
||||
var PrecompiledContractsXDCv2 = map[common.Address]PrecompiledContract{
|
||||
common.BytesToAddress([]byte{1}): &ecrecover{},
|
||||
common.BytesToAddress([]byte{2}): &sha256hash{},
|
||||
common.BytesToAddress([]byte{3}): &ripemd160hash{},
|
||||
common.BytesToAddress([]byte{4}): &dataCopy{},
|
||||
common.BytesToAddress([]byte{5}): &bigModExp{},
|
||||
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
|
||||
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
|
||||
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
|
||||
common.BytesToAddress([]byte{9}): &blake2F{},
|
||||
}
|
||||
|
||||
var (
|
||||
PrecompiledAddressesXDCv2 []common.Address
|
||||
PrecompiledAddressesIstanbul []common.Address
|
||||
PrecompiledAddressesByzantium []common.Address
|
||||
PrecompiledAddressesHomestead []common.Address
|
||||
)
|
||||
|
||||
func init() {
|
||||
for k := range PrecompiledContractsHomestead {
|
||||
PrecompiledAddressesHomestead = append(PrecompiledAddressesHomestead, k)
|
||||
}
|
||||
for k := range PrecompiledContractsByzantium {
|
||||
PrecompiledAddressesHomestead = append(PrecompiledAddressesByzantium, k)
|
||||
}
|
||||
for k := range PrecompiledContractsIstanbul {
|
||||
PrecompiledAddressesIstanbul = append(PrecompiledAddressesIstanbul, k)
|
||||
}
|
||||
for k := range PrecompiledContractsXDCv2 {
|
||||
PrecompiledAddressesXDCv2 = append(PrecompiledAddressesXDCv2, k)
|
||||
}
|
||||
}
|
||||
|
||||
// ActivePrecompiles returns the precompiles enabled with the current configuration.
|
||||
func ActivePrecompiles(rules params.Rules) []common.Address {
|
||||
switch {
|
||||
case rules.IsXDCxDisable:
|
||||
return PrecompiledAddressesXDCv2
|
||||
case rules.IsIstanbul:
|
||||
return PrecompiledAddressesIstanbul
|
||||
case rules.IsByzantium:
|
||||
return PrecompiledAddressesByzantium
|
||||
default:
|
||||
return PrecompiledAddressesHomestead
|
||||
}
|
||||
}
|
||||
|
||||
// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
|
||||
func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
|
||||
gas := p.RequiredGas(input)
|
||||
|
|
@ -488,7 +535,6 @@ func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) {
|
|||
return runBn256Pairing(input)
|
||||
}
|
||||
|
||||
|
||||
type blake2F struct{}
|
||||
|
||||
func (c *blake2F) RequiredGas(input []byte) uint64 {
|
||||
|
|
|
|||
|
|
@ -357,9 +357,15 @@ var bn256PairingTests = []precompiledTest{
|
|||
}
|
||||
|
||||
var XDCxLastPriceTests = []precompiledTest{
|
||||
// {
|
||||
// input: common.Bytes2Hex(append(common.Hex2BytesFixed(BTCAddress, 32), common.Hex2BytesFixed(USDTAddress, 32)...)),
|
||||
// expected: common.Bytes2Hex(common.LeftPadBytes(BTCUSDTLastPrice.Bytes(), XDCXPriceNumberOfBytesReturn)),
|
||||
// name: "BTCUSDT",
|
||||
// },
|
||||
// since we diable XDCx precompiles, the test now returns 0
|
||||
{
|
||||
input: common.Bytes2Hex(append(common.Hex2BytesFixed(BTCAddress, 32), common.Hex2BytesFixed(USDTAddress, 32)...)),
|
||||
expected: common.Bytes2Hex(common.LeftPadBytes(BTCUSDTLastPrice.Bytes(), XDCXPriceNumberOfBytesReturn)),
|
||||
expected: common.Bytes2Hex(common.LeftPadBytes(common.Big0.Bytes(), XDCXPriceNumberOfBytesReturn)),
|
||||
name: "BTCUSDT",
|
||||
},
|
||||
{
|
||||
|
|
@ -375,9 +381,15 @@ var XDCxLastPriceTests = []precompiledTest{
|
|||
}
|
||||
|
||||
var XDCxEpochPriceTests = []precompiledTest{
|
||||
// {
|
||||
// input: common.Bytes2Hex(append(common.Hex2BytesFixed(BTCAddress, 32), common.Hex2BytesFixed(USDTAddress, 32)...)),
|
||||
// expected: common.Bytes2Hex(common.LeftPadBytes(BTCUSDTEpochPrice.Bytes(), XDCXPriceNumberOfBytesReturn)),
|
||||
// name: "BTCUSDT",
|
||||
// },
|
||||
// since we diable XDCx precompiles, the test now returns 0
|
||||
{
|
||||
input: common.Bytes2Hex(append(common.Hex2BytesFixed(BTCAddress, 32), common.Hex2BytesFixed(USDTAddress, 32)...)),
|
||||
expected: common.Bytes2Hex(common.LeftPadBytes(BTCUSDTEpochPrice.Bytes(), XDCXPriceNumberOfBytesReturn)),
|
||||
expected: common.Bytes2Hex(common.LeftPadBytes(common.Big0.Bytes(), XDCXPriceNumberOfBytesReturn)),
|
||||
name: "BTCUSDT",
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ func EnableEIP(eipNum int, jt *JumpTable) error {
|
|||
enable3855(jt)
|
||||
case 3198:
|
||||
enable3198(jt)
|
||||
case 2929:
|
||||
enable2929(jt)
|
||||
case 2200:
|
||||
enable2200(jt)
|
||||
case 1884:
|
||||
|
|
@ -65,9 +67,9 @@ func enable1884(jt *JumpTable) {
|
|||
}
|
||||
}
|
||||
|
||||
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(callContext.contract.Address()))
|
||||
callContext.stack.push(balance)
|
||||
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(scope.Contract.Address()))
|
||||
scope.Stack.push(balance)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
@ -84,9 +86,9 @@ func enable1344(jt *JumpTable) {
|
|||
}
|
||||
|
||||
// opChainID implements CHAINID opcode
|
||||
func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opChainID(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainId)
|
||||
callContext.stack.push(chainId)
|
||||
scope.Stack.push(chainId)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
@ -96,6 +98,44 @@ func enable2200(jt *JumpTable) {
|
|||
jt[SSTORE].dynamicGas = gasSStoreEIP2200
|
||||
}
|
||||
|
||||
// enable2929 enables "EIP-2929: Gas cost increases for state access opcodes"
|
||||
// https://eips.ethereum.org/EIPS/eip-2929
|
||||
func enable2929(jt *JumpTable) {
|
||||
jt[SSTORE].dynamicGas = gasSStoreEIP2929
|
||||
|
||||
jt[SLOAD].constantGas = 0
|
||||
jt[SLOAD].dynamicGas = gasSLoadEIP2929
|
||||
|
||||
jt[EXTCODECOPY].constantGas = WarmStorageReadCostEIP2929
|
||||
jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP2929
|
||||
|
||||
jt[EXTCODESIZE].constantGas = WarmStorageReadCostEIP2929
|
||||
jt[EXTCODESIZE].dynamicGas = gasEip2929AccountCheck
|
||||
|
||||
jt[EXTCODEHASH].constantGas = WarmStorageReadCostEIP2929
|
||||
jt[EXTCODEHASH].dynamicGas = gasEip2929AccountCheck
|
||||
|
||||
jt[BALANCE].constantGas = WarmStorageReadCostEIP2929
|
||||
jt[BALANCE].dynamicGas = gasEip2929AccountCheck
|
||||
|
||||
jt[CALL].constantGas = WarmStorageReadCostEIP2929
|
||||
jt[CALL].dynamicGas = gasCallEIP2929
|
||||
|
||||
jt[CALLCODE].constantGas = WarmStorageReadCostEIP2929
|
||||
jt[CALLCODE].dynamicGas = gasCallCodeEIP2929
|
||||
|
||||
jt[STATICCALL].constantGas = WarmStorageReadCostEIP2929
|
||||
jt[STATICCALL].dynamicGas = gasStaticCallEIP2929
|
||||
|
||||
jt[DELEGATECALL].constantGas = WarmStorageReadCostEIP2929
|
||||
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929
|
||||
|
||||
// This was previously part of the dynamic cost, but we're using it as a constantGas
|
||||
// factor here
|
||||
jt[SELFDESTRUCT].constantGas = params.SelfdestructGasEIP150
|
||||
jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP2929
|
||||
}
|
||||
|
||||
// enable3198 applies EIP-3198 (BASEFEE Opcode)
|
||||
// - Adds an opcode that returns the current block's base fee.
|
||||
func enable3198(jt *JumpTable) {
|
||||
|
|
@ -109,9 +149,9 @@ func enable3198(jt *JumpTable) {
|
|||
}
|
||||
|
||||
// opBaseFee implements BASEFEE opcode
|
||||
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) {
|
||||
baseFee, _ := uint256.FromBig(common.MinGasPrice50x)
|
||||
callContext.stack.push(baseFee)
|
||||
callContext.Stack.push(baseFee)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +167,7 @@ func enable3855(jt *JumpTable) {
|
|||
}
|
||||
|
||||
// opPush0 implements the PUSH0 opcode
|
||||
func opPush0(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int))
|
||||
func opPush0(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) {
|
||||
callContext.Stack.push(new(uint256.Int))
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,10 +23,9 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
)
|
||||
|
||||
// emptyCodeHash is used by create to ensure deployment is disallowed to already
|
||||
|
|
@ -43,24 +42,49 @@ type (
|
|||
GetHashFunc func(uint64) common.Hash
|
||||
)
|
||||
|
||||
func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
|
||||
var precompiles map[common.Address]PrecompiledContract
|
||||
switch {
|
||||
case evm.chainRules.IsXDCxDisable:
|
||||
precompiles = PrecompiledContractsXDCv2
|
||||
case evm.chainRules.IsIstanbul:
|
||||
precompiles = PrecompiledContractsIstanbul
|
||||
case evm.chainRules.IsByzantium:
|
||||
precompiles = PrecompiledContractsByzantium
|
||||
default:
|
||||
precompiles = PrecompiledContractsHomestead
|
||||
}
|
||||
p, ok := precompiles[addr]
|
||||
return p, ok
|
||||
}
|
||||
|
||||
func (evm *EVM) precompile2(addr common.Address) (PrecompiledContract, bool) {
|
||||
var precompiles map[common.Address]PrecompiledContract
|
||||
switch {
|
||||
case evm.chainRules.IsXDCxDisable:
|
||||
precompiles = PrecompiledContractsXDCv2
|
||||
case evm.chainRules.IsIstanbul && evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber):
|
||||
precompiles = PrecompiledContractsIstanbul
|
||||
case evm.chainRules.IsByzantium:
|
||||
precompiles = PrecompiledContractsByzantium
|
||||
default:
|
||||
precompiles = PrecompiledContractsHomestead
|
||||
}
|
||||
p, ok := precompiles[addr]
|
||||
return p, ok
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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) {
|
||||
case *XDCxEpochPrice:
|
||||
p.(*XDCxEpochPrice).SetTradingState(evm.tradingStateDB)
|
||||
case *XDCxLastPrice:
|
||||
p.(*XDCxLastPrice).SetTradingState(evm.tradingStateDB)
|
||||
if p, isPrecompile := evm.precompile(*contract.CodeAddr); isPrecompile {
|
||||
if evm.chainConfig.IsTIPXDCXReceiver(evm.BlockNumber) {
|
||||
switch p := p.(type) {
|
||||
case *XDCxEpochPrice:
|
||||
p.SetTradingState(evm.tradingStateDB)
|
||||
case *XDCxLastPrice:
|
||||
p.SetTradingState(evm.tradingStateDB)
|
||||
}
|
||||
}
|
||||
return RunPrecompiledContract(p, input, contract)
|
||||
}
|
||||
|
|
@ -205,19 +229,11 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
|||
snapshot = evm.StateDB.Snapshot()
|
||||
)
|
||||
if !evm.StateDB.Exist(addr) {
|
||||
precompiles := PrecompiledContractsHomestead
|
||||
if evm.chainRules.IsByzantium {
|
||||
precompiles = PrecompiledContractsByzantium
|
||||
}
|
||||
if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber) {
|
||||
if evm.chainRules.IsIstanbul {
|
||||
precompiles = PrecompiledContractsIstanbul
|
||||
}
|
||||
}
|
||||
if precompiles[addr] == nil && evm.chainRules.IsEIP158 && value.Sign() == 0 {
|
||||
_, isPrecompile := evm.precompile2(addr)
|
||||
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
|
||||
// Calling a non existing account, don't do anything, but ping the tracer
|
||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
|
||||
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
|
||||
evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil)
|
||||
}
|
||||
return nil, gas, nil
|
||||
|
|
@ -235,7 +251,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
|||
|
||||
// Capture the tracer start/end events in debug mode
|
||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
|
||||
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
|
||||
|
||||
defer func() { // Lazy evaluation of the parameters
|
||||
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
||||
|
|
@ -384,7 +400,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||
}
|
||||
nonce := evm.StateDB.GetNonce(caller.Address())
|
||||
evm.StateDB.SetNonce(caller.Address(), nonce+1)
|
||||
|
||||
// We add this to the access list _before_ taking a snapshot. Even if the creation fails,
|
||||
// the access-list change should not be rolled back
|
||||
if evm.chainRules.IsEIP1559 {
|
||||
evm.StateDB.AddAddressToAccessList(address)
|
||||
}
|
||||
// Ensure there's no existing contract already at the designated address
|
||||
contractHash := evm.StateDB.GetCodeHash(address)
|
||||
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
|
||||
|
|
@ -404,7 +424,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||
contract.SetCodeOptionalHash(&address, codeAndHash)
|
||||
|
||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value)
|
||||
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
|
||||
}
|
||||
start := time.Now()
|
||||
|
||||
|
|
|
|||
|
|
@ -26,68 +26,68 @@ import (
|
|||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
func opAdd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.Add(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opSub(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.Sub(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMul(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opMul(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.Mul(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opDiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opDiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.Div(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSdiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opSdiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.SDiv(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opMod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.Mod(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opSmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.SMod(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
base, exponent := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opExp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
base, exponent := scope.Stack.pop(), scope.Stack.peek()
|
||||
exponent.Exp(&base, exponent)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSignExtend(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
back, num := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opSignExtend(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
back, num := scope.Stack.pop(), scope.Stack.peek()
|
||||
num.ExtendSign(num, &back)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opNot(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x := callContext.stack.peek()
|
||||
func opNot(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x := scope.Stack.peek()
|
||||
x.Not(x)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opLt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
if x.Lt(y) {
|
||||
y.SetOne()
|
||||
} else {
|
||||
|
|
@ -96,8 +96,8 @@ func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opGt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
if x.Gt(y) {
|
||||
y.SetOne()
|
||||
} else {
|
||||
|
|
@ -106,8 +106,8 @@ func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opSlt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
if x.Slt(y) {
|
||||
y.SetOne()
|
||||
} else {
|
||||
|
|
@ -116,8 +116,8 @@ func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opSgt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
if x.Sgt(y) {
|
||||
y.SetOne()
|
||||
} else {
|
||||
|
|
@ -126,8 +126,8 @@ func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opEq(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
if x.Eq(y) {
|
||||
y.SetOne()
|
||||
} else {
|
||||
|
|
@ -136,8 +136,8 @@ func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x := callContext.stack.peek()
|
||||
func opIszero(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x := scope.Stack.peek()
|
||||
if x.IsZero() {
|
||||
x.SetOne()
|
||||
} else {
|
||||
|
|
@ -146,32 +146,32 @@ func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opAnd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opAnd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.And(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opOr(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opOr(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.Or(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opXor(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opXor(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.Xor(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opByte(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
th, val := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opByte(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
th, val := scope.Stack.pop(), scope.Stack.peek()
|
||||
val.Byte(&th)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek()
|
||||
func opAddmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek()
|
||||
if z.IsZero() {
|
||||
z.Clear()
|
||||
} else {
|
||||
|
|
@ -180,8 +180,8 @@ func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek()
|
||||
func opMulmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek()
|
||||
z.MulMod(&x, &y, z)
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -189,9 +189,9 @@ func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
|
|||
// opSHL implements Shift Left
|
||||
// The SHL instruction (shift left) pops 2 values from the stack, first arg1 and then arg2,
|
||||
// and pushes on the stack arg2 shifted to the left by arg1 number of bits.
|
||||
func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opSHL(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
|
||||
shift, value := callContext.stack.pop(), callContext.stack.peek()
|
||||
shift, value := scope.Stack.pop(), scope.Stack.peek()
|
||||
if shift.LtUint64(256) {
|
||||
value.Lsh(value, uint(shift.Uint64()))
|
||||
} else {
|
||||
|
|
@ -203,9 +203,9 @@ func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
|
|||
// opSHR implements Logical Shift Right
|
||||
// The SHR instruction (logical shift right) pops 2 values from the stack, first arg1 and then arg2,
|
||||
// and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill.
|
||||
func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opSHR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
|
||||
shift, value := callContext.stack.pop(), callContext.stack.peek()
|
||||
shift, value := scope.Stack.pop(), scope.Stack.peek()
|
||||
if shift.LtUint64(256) {
|
||||
value.Rsh(value, uint(shift.Uint64()))
|
||||
} else {
|
||||
|
|
@ -217,8 +217,8 @@ func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
|
|||
// opSAR implements Arithmetic Shift Right
|
||||
// The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2,
|
||||
// and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension.
|
||||
func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
shift, value := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opSAR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
shift, value := scope.Stack.pop(), scope.Stack.peek()
|
||||
if shift.GtUint64(256) {
|
||||
if value.Sign() >= 0 {
|
||||
value.Clear()
|
||||
|
|
@ -233,9 +233,9 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opKeccak256(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
offset, size := callContext.stack.pop(), callContext.stack.peek()
|
||||
data := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
offset, size := scope.Stack.pop(), scope.Stack.peek()
|
||||
data := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
|
||||
if interpreter.hasher == nil {
|
||||
interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState)
|
||||
|
|
@ -253,37 +253,37 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
|
|||
size.SetBytes(interpreter.hasherBuf[:])
|
||||
return nil, nil
|
||||
}
|
||||
func opAddress(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Address().Bytes()))
|
||||
func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Address().Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
slot := callContext.stack.peek()
|
||||
func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
slot := scope.Stack.peek()
|
||||
address := common.Address(slot.Bytes20())
|
||||
slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opOrigin(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes()))
|
||||
func opOrigin(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
func opCaller(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Caller().Bytes()))
|
||||
func opCaller(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Caller().Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCallValue(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
v, _ := uint256.FromBig(callContext.contract.value)
|
||||
callContext.stack.push(v)
|
||||
func opCallValue(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
v, _ := uint256.FromBig(scope.Contract.value)
|
||||
scope.Stack.push(v)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x := callContext.stack.peek()
|
||||
func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x := scope.Stack.peek()
|
||||
if offset, overflow := x.Uint64WithOverflow(); !overflow {
|
||||
data := getData(callContext.contract.Input, offset, 32)
|
||||
data := getData(scope.Contract.Input, offset, 32)
|
||||
x.SetBytes(data)
|
||||
} else {
|
||||
x.Clear()
|
||||
|
|
@ -291,16 +291,16 @@ func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(callContext.contract.Input))))
|
||||
func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Input))))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
memOffset = callContext.stack.pop()
|
||||
dataOffset = callContext.stack.pop()
|
||||
length = callContext.stack.pop()
|
||||
memOffset = scope.Stack.pop()
|
||||
dataOffset = scope.Stack.pop()
|
||||
length = scope.Stack.pop()
|
||||
)
|
||||
dataOffset64, overflow := dataOffset.Uint64WithOverflow()
|
||||
if overflow {
|
||||
|
|
@ -309,21 +309,21 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
|
|||
// These values are checked for overflow during gas cost calculation
|
||||
memOffset64 := memOffset.Uint64()
|
||||
length64 := length.Uint64()
|
||||
callContext.memory.Set(memOffset64, length64, getData(callContext.contract.Input, dataOffset64, length64))
|
||||
scope.Memory.Set(memOffset64, length64, getData(scope.Contract.Input, dataOffset64, length64))
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData))))
|
||||
func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData))))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
memOffset = callContext.stack.pop()
|
||||
dataOffset = callContext.stack.pop()
|
||||
length = callContext.stack.pop()
|
||||
memOffset = scope.Stack.pop()
|
||||
dataOffset = scope.Stack.pop()
|
||||
length = scope.Stack.pop()
|
||||
)
|
||||
|
||||
offset64, overflow := dataOffset.Uint64WithOverflow()
|
||||
|
|
@ -337,42 +337,42 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *call
|
|||
if overflow || uint64(len(interpreter.returnData)) < end64 {
|
||||
return nil, ErrReturnDataOutOfBounds
|
||||
}
|
||||
callContext.memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[offset64:end64])
|
||||
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[offset64:end64])
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
slot := callContext.stack.peek()
|
||||
func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
slot := scope.Stack.peek()
|
||||
slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.Address(slot.Bytes20()))))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
l := new(uint256.Int)
|
||||
l.SetUint64(uint64(len(callContext.contract.Code)))
|
||||
callContext.stack.push(l)
|
||||
l.SetUint64(uint64(len(scope.Contract.Code)))
|
||||
scope.Stack.push(l)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
memOffset = callContext.stack.pop()
|
||||
codeOffset = callContext.stack.pop()
|
||||
length = callContext.stack.pop()
|
||||
memOffset = scope.Stack.pop()
|
||||
codeOffset = scope.Stack.pop()
|
||||
length = scope.Stack.pop()
|
||||
)
|
||||
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
|
||||
if overflow {
|
||||
uint64CodeOffset = 0xffffffffffffffff
|
||||
}
|
||||
codeCopy := getData(callContext.contract.Code, uint64CodeOffset, length.Uint64())
|
||||
callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
|
||||
codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64())
|
||||
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
stack = callContext.stack
|
||||
stack = scope.Stack
|
||||
a = stack.pop()
|
||||
memOffset = stack.pop()
|
||||
codeOffset = stack.pop()
|
||||
|
|
@ -384,7 +384,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
|
|||
}
|
||||
addr := common.Address(a.Bytes20())
|
||||
codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64())
|
||||
callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
|
||||
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -392,16 +392,21 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
|
|||
// opExtCodeHash returns the code hash of a specified account.
|
||||
// There are several cases when the function is called, while we can relay everything
|
||||
// to `state.GetCodeHash` function to ensure the correctness.
|
||||
// (1) Caller tries to get the code hash of a normal contract account, state
|
||||
//
|
||||
// (1) Caller tries to get the code hash of a normal contract account, state
|
||||
//
|
||||
// should return the relative code hash and set it as the result.
|
||||
//
|
||||
// (2) Caller tries to get the code hash of a non-existent account, state should
|
||||
// (2) Caller tries to get the code hash of a non-existent account, state should
|
||||
//
|
||||
// return common.Hash{} and zero will be set as the result.
|
||||
//
|
||||
// (3) Caller tries to get the code hash for an account without contract code,
|
||||
// (3) Caller tries to get the code hash for an account without contract code,
|
||||
//
|
||||
// state should return emptyCodeHash(0xc5d246...) as the result.
|
||||
//
|
||||
// (4) Caller tries to get the code hash of a precompiled account, the result
|
||||
// (4) Caller tries to get the code hash of a precompiled account, the result
|
||||
//
|
||||
// should be zero or emptyCodeHash.
|
||||
//
|
||||
// It is worth noting that in order to avoid unnecessary create and clean,
|
||||
|
|
@ -410,13 +415,15 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
|
|||
// If the precompile account is not transferred any amount on a private or
|
||||
// customized chain, the return value will be zero.
|
||||
//
|
||||
// (5) Caller tries to get the code hash for an account which is marked as suicided
|
||||
// (5) Caller tries to get the code hash for an account which is marked as suicided
|
||||
//
|
||||
// in the current transaction, the code hash of this account should be returned.
|
||||
//
|
||||
// (6) Caller tries to get the code hash for an account which is marked as deleted,
|
||||
// (6) Caller tries to get the code hash for an account which is marked as deleted,
|
||||
//
|
||||
// this account should be regarded as a non-existent account and zero should be returned.
|
||||
func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
slot := callContext.stack.peek()
|
||||
func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
slot := scope.Stack.peek()
|
||||
address := common.Address(slot.Bytes20())
|
||||
if interpreter.evm.StateDB.Empty(address) {
|
||||
slot.Clear()
|
||||
|
|
@ -426,14 +433,14 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opGasprice(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opGasprice(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
v, _ := uint256.FromBig(interpreter.evm.GasPrice)
|
||||
callContext.stack.push(v)
|
||||
scope.Stack.push(v)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
num := callContext.stack.peek()
|
||||
func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
num := scope.Stack.peek()
|
||||
num64, overflow := num.Uint64WithOverflow()
|
||||
if overflow {
|
||||
num.Clear()
|
||||
|
|
@ -454,108 +461,108 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Coinbase.Bytes()))
|
||||
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Coinbase.Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opTimestamp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opTimestamp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
v, _ := uint256.FromBig(interpreter.evm.Time)
|
||||
callContext.stack.push(v)
|
||||
scope.Stack.push(v)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opNumber(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opNumber(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
v, _ := uint256.FromBig(interpreter.evm.BlockNumber)
|
||||
callContext.stack.push(v)
|
||||
scope.Stack.push(v)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opDifficulty(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
v, _ := uint256.FromBig(interpreter.evm.Difficulty)
|
||||
callContext.stack.push(v)
|
||||
scope.Stack.push(v)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opRandom(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opRandom(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var v *uint256.Int
|
||||
if interpreter.evm.Context.Random != nil {
|
||||
v = new(uint256.Int).SetBytes((interpreter.evm.Context.Random.Bytes()))
|
||||
} else { // if context random is not set, use emptyCodeHash as default
|
||||
v = new(uint256.Int).SetBytes(emptyCodeHash.Bytes())
|
||||
}
|
||||
callContext.stack.push(v)
|
||||
scope.Stack.push(v)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit))
|
||||
func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opPop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.pop()
|
||||
func opPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.pop()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
v := callContext.stack.peek()
|
||||
func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
v := scope.Stack.peek()
|
||||
offset := int64(v.Uint64())
|
||||
v.SetBytes(callContext.memory.GetPtr(offset, 32))
|
||||
v.SetBytes(scope.Memory.GetPtr(offset, 32))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opMstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
// pop value of the stack
|
||||
mStart, val := callContext.stack.pop(), callContext.stack.pop()
|
||||
callContext.memory.Set32(mStart.Uint64(), &val)
|
||||
mStart, val := scope.Stack.pop(), scope.Stack.pop()
|
||||
scope.Memory.Set32(mStart.Uint64(), &val)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
off, val := callContext.stack.pop(), callContext.stack.pop()
|
||||
callContext.memory.store[off.Uint64()] = byte(val.Uint64())
|
||||
func opMstore8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
off, val := scope.Stack.pop(), scope.Stack.pop()
|
||||
scope.Memory.store[off.Uint64()] = byte(val.Uint64())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
loc := callContext.stack.peek()
|
||||
func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
loc := scope.Stack.peek()
|
||||
hash := common.Hash(loc.Bytes32())
|
||||
val := interpreter.evm.StateDB.GetState(callContext.contract.Address(), hash)
|
||||
val := interpreter.evm.StateDB.GetState(scope.Contract.Address(), hash)
|
||||
loc.SetBytes(val.Bytes())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
if interpreter.readOnly {
|
||||
return nil, ErrWriteProtection
|
||||
}
|
||||
loc := callContext.stack.pop()
|
||||
val := callContext.stack.pop()
|
||||
interpreter.evm.StateDB.SetState(callContext.contract.Address(),
|
||||
loc := scope.Stack.pop()
|
||||
val := scope.Stack.pop()
|
||||
interpreter.evm.StateDB.SetState(scope.Contract.Address(),
|
||||
common.Hash(loc.Bytes32()), common.Hash(val.Bytes32()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opJump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
if atomic.LoadInt32(&interpreter.evm.abort) != 0 {
|
||||
return nil, errStopToken
|
||||
}
|
||||
pos := callContext.stack.pop()
|
||||
if !callContext.contract.validJumpdest(&pos) {
|
||||
pos := scope.Stack.pop()
|
||||
if !scope.Contract.validJumpdest(&pos) {
|
||||
return nil, ErrInvalidJump
|
||||
}
|
||||
*pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opJumpi(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
if atomic.LoadInt32(&interpreter.evm.abort) != 0 {
|
||||
return nil, errStopToken
|
||||
}
|
||||
pos, cond := callContext.stack.pop(), callContext.stack.pop()
|
||||
pos, cond := scope.Stack.pop(), scope.Stack.pop()
|
||||
if !cond.IsZero() {
|
||||
if !callContext.contract.validJumpdest(&pos) {
|
||||
if !scope.Contract.validJumpdest(&pos) {
|
||||
return nil, ErrInvalidJump
|
||||
}
|
||||
*pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop
|
||||
|
|
@ -563,34 +570,34 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]b
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opJumpdest(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opJumpdest(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetUint64(*pc))
|
||||
func opPc(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetUint64(*pc))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMsize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetUint64(uint64(callContext.memory.Len())))
|
||||
func opMsize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetUint64(uint64(scope.Memory.Len())))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opGas(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetUint64(callContext.contract.Gas))
|
||||
func opGas(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetUint64(scope.Contract.Gas))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
if interpreter.readOnly {
|
||||
return nil, ErrWriteProtection
|
||||
}
|
||||
var (
|
||||
value = callContext.stack.pop()
|
||||
offset, size = callContext.stack.pop(), callContext.stack.pop()
|
||||
input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
gas = callContext.contract.Gas
|
||||
value = scope.Stack.pop()
|
||||
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
||||
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
gas = scope.Contract.Gas
|
||||
)
|
||||
if interpreter.evm.chainRules.IsEIP150 {
|
||||
gas -= gas / 64
|
||||
|
|
@ -598,8 +605,8 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
|
|||
// reuse size int for stackvalue
|
||||
stackvalue := size
|
||||
|
||||
callContext.contract.UseGas(gas)
|
||||
res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, value.ToBig())
|
||||
scope.Contract.UseGas(gas)
|
||||
res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, value.ToBig())
|
||||
// Push item on the stack based on the returned error. If the ruleset is
|
||||
// homestead we must check for CodeStoreOutOfGasError (homestead only
|
||||
// rule) and treat as an error, if the ruleset is frontier we must
|
||||
|
|
@ -611,8 +618,8 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
|
|||
} else {
|
||||
stackvalue.SetBytes(addr.Bytes())
|
||||
}
|
||||
callContext.stack.push(&stackvalue)
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Stack.push(&stackvalue)
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
if suberr == ErrExecutionReverted {
|
||||
interpreter.returnData = res // set REVERT data to return data buffer
|
||||
|
|
@ -622,24 +629,24 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
if interpreter.readOnly {
|
||||
return nil, ErrWriteProtection
|
||||
}
|
||||
var (
|
||||
endowment = callContext.stack.pop()
|
||||
offset, size = callContext.stack.pop(), callContext.stack.pop()
|
||||
salt = callContext.stack.pop()
|
||||
input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
gas = callContext.contract.Gas
|
||||
endowment = scope.Stack.pop()
|
||||
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
||||
salt = scope.Stack.pop()
|
||||
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
gas = scope.Contract.Gas
|
||||
)
|
||||
|
||||
// Apply EIP150
|
||||
gas -= gas / 64
|
||||
callContext.contract.UseGas(gas)
|
||||
scope.Contract.UseGas(gas)
|
||||
// reuse size int for stackvalue
|
||||
stackvalue := size
|
||||
res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas,
|
||||
res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas,
|
||||
endowment.ToBig(), salt.ToBig())
|
||||
// Push item on the stack based on the returned error.
|
||||
if suberr != nil {
|
||||
|
|
@ -647,8 +654,8 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
|
|||
} else {
|
||||
stackvalue.SetBytes(addr.Bytes())
|
||||
}
|
||||
callContext.stack.push(&stackvalue)
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Stack.push(&stackvalue)
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
if suberr == ErrExecutionReverted {
|
||||
interpreter.returnData = res // set REVERT data to return data buffer
|
||||
|
|
@ -658,8 +665,8 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
stack := callContext.stack
|
||||
func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
stack := scope.Stack
|
||||
// Pop gas. The actual gas in interpreter.evm.callGasTemp.
|
||||
// We can use this as a temporary value
|
||||
temp := stack.pop()
|
||||
|
|
@ -668,7 +675,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
|
|||
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||
toAddr := common.Address(addr.Bytes20())
|
||||
// Get the arguments from the memory.
|
||||
args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
||||
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
||||
|
||||
if interpreter.readOnly && !value.IsZero() {
|
||||
return nil, ErrWriteProtection
|
||||
|
|
@ -676,7 +683,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
|
|||
if !value.IsZero() {
|
||||
gas += params.CallStipend
|
||||
}
|
||||
ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, value.ToBig())
|
||||
ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, value.ToBig())
|
||||
if err != nil {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
@ -685,17 +692,17 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
|
|||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
interpreter.returnData = ret
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
|
||||
stack := callContext.stack
|
||||
stack := scope.Stack
|
||||
// We use it as a temporary value
|
||||
temp := stack.pop()
|
||||
gas := interpreter.evm.callGasTemp
|
||||
|
|
@ -703,12 +710,12 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
|
|||
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||
toAddr := common.Address(addr.Bytes20())
|
||||
// Get arguments from the memory.
|
||||
args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
||||
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
||||
|
||||
if !value.IsZero() {
|
||||
gas += params.CallStipend
|
||||
}
|
||||
ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, value.ToBig())
|
||||
ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, value.ToBig())
|
||||
if err != nil {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
@ -717,16 +724,16 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
|
|||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
interpreter.returnData = ret
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
stack := callContext.stack
|
||||
func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
stack := scope.Stack
|
||||
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
|
||||
// We use it as a temporary value
|
||||
temp := stack.pop()
|
||||
|
|
@ -735,9 +742,9 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
|
|||
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||
toAddr := common.Address(addr.Bytes20())
|
||||
// Get arguments from the memory.
|
||||
args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
||||
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
||||
|
||||
ret, returnGas, err := interpreter.evm.DelegateCall(callContext.contract, toAddr, args, gas)
|
||||
ret, returnGas, err := interpreter.evm.DelegateCall(scope.Contract, toAddr, args, gas)
|
||||
if err != nil {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
@ -746,17 +753,17 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
|
|||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
interpreter.returnData = ret
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
|
||||
stack := callContext.stack
|
||||
stack := scope.Stack
|
||||
// We use it as a temporary value
|
||||
temp := stack.pop()
|
||||
gas := interpreter.evm.callGasTemp
|
||||
|
|
@ -764,9 +771,9 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
|
|||
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||
toAddr := common.Address(addr.Bytes20())
|
||||
// Get arguments from the memory.
|
||||
args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
||||
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
||||
|
||||
ret, returnGas, err := interpreter.evm.StaticCall(callContext.contract, toAddr, args, gas)
|
||||
ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas)
|
||||
if err != nil {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
@ -775,45 +782,45 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
|
|||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
interpreter.returnData = ret
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func opReturn(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
offset, size := callContext.stack.pop(), callContext.stack.pop()
|
||||
ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
||||
ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
|
||||
return ret, errStopToken
|
||||
}
|
||||
|
||||
func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
offset, size := callContext.stack.pop(), callContext.stack.pop()
|
||||
ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
||||
ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
|
||||
interpreter.returnData = ret
|
||||
return ret, ErrExecutionReverted
|
||||
}
|
||||
|
||||
func opUndefined(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
return nil, &ErrInvalidOpCode{opcode: OpCode(callContext.contract.Code[*pc])}
|
||||
func opUndefined(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
return nil, &ErrInvalidOpCode{opcode: OpCode(scope.Contract.Code[*pc])}
|
||||
}
|
||||
|
||||
func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opStop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
return nil, errStopToken
|
||||
}
|
||||
|
||||
func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
if interpreter.readOnly {
|
||||
return nil, ErrWriteProtection
|
||||
}
|
||||
beneficiary := callContext.stack.pop()
|
||||
balance := interpreter.evm.StateDB.GetBalance(callContext.contract.Address())
|
||||
beneficiary := scope.Stack.pop()
|
||||
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
|
||||
interpreter.evm.StateDB.AddBalance(common.Address(beneficiary.Bytes20()), balance)
|
||||
interpreter.evm.StateDB.Suicide(callContext.contract.Address())
|
||||
interpreter.evm.StateDB.Suicide(scope.Contract.Address())
|
||||
return nil, errStopToken
|
||||
}
|
||||
|
||||
|
|
@ -821,21 +828,21 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
|
|||
|
||||
// make log instruction function
|
||||
func makeLog(size int) executionFunc {
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
if interpreter.readOnly {
|
||||
return nil, ErrWriteProtection
|
||||
}
|
||||
topics := make([]common.Hash, size)
|
||||
stack := callContext.stack
|
||||
stack := scope.Stack
|
||||
mStart, mSize := stack.pop(), stack.pop()
|
||||
for i := 0; i < size; i++ {
|
||||
addr := stack.pop()
|
||||
topics[i] = common.Hash(addr.Bytes32())
|
||||
}
|
||||
|
||||
d := callContext.memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64()))
|
||||
d := scope.Memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64()))
|
||||
interpreter.evm.StateDB.AddLog(&types.Log{
|
||||
Address: callContext.contract.Address(),
|
||||
Address: scope.Contract.Address(),
|
||||
Topics: topics,
|
||||
Data: d,
|
||||
// This is a non-consensus field, but assigned here because
|
||||
|
|
@ -848,24 +855,24 @@ func makeLog(size int) executionFunc {
|
|||
}
|
||||
|
||||
// opPush1 is a specialized version of pushN
|
||||
func opPush1(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
codeLen = uint64(len(callContext.contract.Code))
|
||||
codeLen = uint64(len(scope.Contract.Code))
|
||||
integer = new(uint256.Int)
|
||||
)
|
||||
*pc += 1
|
||||
if *pc < codeLen {
|
||||
callContext.stack.push(integer.SetUint64(uint64(callContext.contract.Code[*pc])))
|
||||
scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc])))
|
||||
} else {
|
||||
callContext.stack.push(integer.Clear())
|
||||
scope.Stack.push(integer.Clear())
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// make push instruction function
|
||||
func makePush(size uint64, pushByteSize int) executionFunc {
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
codeLen := len(callContext.contract.Code)
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
codeLen := len(scope.Contract.Code)
|
||||
|
||||
startMin := codeLen
|
||||
if int(*pc+1) < startMin {
|
||||
|
|
@ -878,8 +885,8 @@ func makePush(size uint64, pushByteSize int) executionFunc {
|
|||
}
|
||||
|
||||
integer := new(uint256.Int)
|
||||
callContext.stack.push(integer.SetBytes(common.RightPadBytes(
|
||||
callContext.contract.Code[startMin:endMin], pushByteSize)))
|
||||
scope.Stack.push(integer.SetBytes(common.RightPadBytes(
|
||||
scope.Contract.Code[startMin:endMin], pushByteSize)))
|
||||
|
||||
*pc += size
|
||||
return nil, nil
|
||||
|
|
@ -888,8 +895,8 @@ func makePush(size uint64, pushByteSize int) executionFunc {
|
|||
|
||||
// make dup instruction function
|
||||
func makeDup(size int64) executionFunc {
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.dup(int(size))
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.dup(int(size))
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
|
@ -898,8 +905,8 @@ func makeDup(size int64) executionFunc {
|
|||
func makeSwap(size int64) executionFunc {
|
||||
// switch n + 1 otherwise n would be swapped with n
|
||||
size++
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.swap(int(size))
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.swap(int(size))
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
|
|||
expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected))
|
||||
stack.push(x)
|
||||
stack.push(y)
|
||||
opFn(&pc, evmInterpreter, &callCtx{nil, stack, nil})
|
||||
opFn(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
||||
if len(stack.data) != 1 {
|
||||
t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data))
|
||||
}
|
||||
|
|
@ -222,7 +222,7 @@ func TestAddMod(t *testing.T) {
|
|||
stack.push(z)
|
||||
stack.push(y)
|
||||
stack.push(x)
|
||||
opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil})
|
||||
opAddmod(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
||||
actual := stack.pop()
|
||||
if actual.Cmp(expected) != 0 {
|
||||
t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual)
|
||||
|
|
@ -244,7 +244,7 @@ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcas
|
|||
y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
|
||||
stack.push(x)
|
||||
stack.push(y)
|
||||
_, err := opFn(&pc, interpreter, &callCtx{nil, stack, nil})
|
||||
_, err := opFn(&pc, interpreter, &ScopeContext{nil, stack, nil})
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
|
@ -308,7 +308,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
|
|||
a.SetBytes(arg)
|
||||
stack.push(a)
|
||||
}
|
||||
op(&pc, evmInterpreter, &callCtx{nil, stack, nil})
|
||||
op(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
||||
stack.pop()
|
||||
}
|
||||
}
|
||||
|
|
@ -535,13 +535,13 @@ func TestOpMstore(t *testing.T) {
|
|||
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
|
||||
stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v)))
|
||||
stack.push(new(uint256.Int))
|
||||
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
|
||||
opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
|
||||
if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v {
|
||||
t.Fatalf("Mstore fail, got %v, expected %v", got, v)
|
||||
}
|
||||
stack.push(new(uint256.Int).SetUint64(0x1))
|
||||
stack.push(new(uint256.Int))
|
||||
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
|
||||
opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
|
||||
if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
|
||||
t.Fatalf("Mstore failed to overwrite previous value")
|
||||
}
|
||||
|
|
@ -565,7 +565,7 @@ func BenchmarkOpMstore(bench *testing.B) {
|
|||
for i := 0; i < bench.N; i++ {
|
||||
stack.push(value)
|
||||
stack.push(memStart)
|
||||
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
|
||||
opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -585,7 +585,7 @@ func BenchmarkOpKeccak256(bench *testing.B) {
|
|||
for i := 0; i < bench.N; i++ {
|
||||
stack.push(uint256.NewInt(32))
|
||||
stack.push(start)
|
||||
opKeccak256(&pc, evmInterpreter, &callCtx{mem, stack, nil})
|
||||
opKeccak256(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -681,7 +681,7 @@ func TestRandom(t *testing.T) {
|
|||
pc = uint64(0)
|
||||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
||||
)
|
||||
opRandom(&pc, evmInterpreter, &callCtx{nil, stack, nil})
|
||||
opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
||||
if len(stack.data) != 1 {
|
||||
t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,16 @@ type StateDB interface {
|
|||
// is defined according to EIP161 (balance = nonce = code = 0).
|
||||
Empty(common.Address) bool
|
||||
|
||||
PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
|
||||
AddressInAccessList(addr common.Address) bool
|
||||
SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool)
|
||||
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
|
||||
// even if the feature/fork is not active yet
|
||||
AddAddressToAccessList(addr common.Address)
|
||||
// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
|
||||
// even if the feature/fork is not active yet
|
||||
AddSlotToAccessList(addr common.Address, slot common.Hash)
|
||||
|
||||
RevertToSnapshot(int)
|
||||
Snapshot() int
|
||||
|
||||
|
|
|
|||
|
|
@ -60,12 +60,12 @@ type Interpreter interface {
|
|||
CanRun([]byte) bool
|
||||
}
|
||||
|
||||
// callCtx contains the things that are per-call, such as stack and memory,
|
||||
// ScopeContext contains the things that are per-call, such as stack and memory,
|
||||
// but not transients like pc and gas
|
||||
type callCtx struct {
|
||||
memory *Memory
|
||||
stack *Stack
|
||||
contract *Contract
|
||||
type ScopeContext struct {
|
||||
Memory *Memory
|
||||
Stack *Stack
|
||||
Contract *Contract
|
||||
}
|
||||
|
||||
// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
|
||||
|
|
@ -93,6 +93,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
|
|||
// If jump table was not initialised we set the default one.
|
||||
if cfg.JumpTable == nil {
|
||||
switch {
|
||||
case evm.chainRules.IsEIP1559:
|
||||
cfg.JumpTable = &eip1559InstructionSet
|
||||
case evm.chainRules.IsShanghai:
|
||||
cfg.JumpTable = &shanghaiInstructionSet
|
||||
case evm.chainRules.IsMerge:
|
||||
|
|
@ -168,10 +170,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
op OpCode // current opcode
|
||||
mem = NewMemory() // bound memory
|
||||
stack = newstack() // local stack
|
||||
callContext = &callCtx{
|
||||
memory: mem,
|
||||
stack: stack,
|
||||
contract: contract,
|
||||
callContext = &ScopeContext{
|
||||
Memory: mem,
|
||||
Stack: stack,
|
||||
Contract: contract,
|
||||
}
|
||||
// For optimisation reason we're using uint64 as the program counter.
|
||||
// It's theoretically possible to go above 2^64. The YP defines the PC
|
||||
|
|
@ -190,9 +192,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
defer func() {
|
||||
if err != nil {
|
||||
if !logged {
|
||||
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
|
||||
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
|
||||
} else {
|
||||
in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
|
||||
in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
|
@ -255,7 +257,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
}
|
||||
|
||||
if in.cfg.Debug {
|
||||
in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
|
||||
in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
|
||||
logged = true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import (
|
|||
)
|
||||
|
||||
type (
|
||||
executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error)
|
||||
executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error)
|
||||
gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
|
||||
// memorySizeFunc returns the required size, and whether the operation overflowed a uint64
|
||||
memorySizeFunc func(*Stack) (size uint64, overflow bool)
|
||||
|
|
@ -54,11 +54,18 @@ var (
|
|||
londonInstructionSet = newLondonInstructionSet()
|
||||
mergeInstructionSet = newMergeInstructionSet()
|
||||
shanghaiInstructionSet = newShanghaiInstructionSet()
|
||||
eip1559InstructionSet = newEip1559InstructionSet()
|
||||
)
|
||||
|
||||
// JumpTable contains the EVM opcodes supported at a given fork.
|
||||
type JumpTable [256]*operation
|
||||
|
||||
func newEip1559InstructionSet() JumpTable {
|
||||
instructionSet := newShanghaiInstructionSet()
|
||||
enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929
|
||||
return instructionSet
|
||||
}
|
||||
|
||||
func newShanghaiInstructionSet() JumpTable {
|
||||
instructionSet := newMergeInstructionSet()
|
||||
enable3855(&instructionSet) // PUSH0 instruction
|
||||
|
|
|
|||
|
|
@ -18,20 +18,19 @@ package vm
|
|||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
)
|
||||
|
||||
var errTraceLimitReached = errors.New("the number of logs reached the specified limit")
|
||||
|
||||
// Storage represents a contract's storage.
|
||||
type Storage map[common.Hash]common.Hash
|
||||
|
||||
|
|
@ -52,6 +51,9 @@ type LogConfig struct {
|
|||
DisableStorage bool // disable storage capture
|
||||
Debug bool // print output during capture end
|
||||
Limit int // maximum length of output, but zero means unlimited
|
||||
|
||||
// Chain overrides, can be used to execute a trace using future fork rules
|
||||
Overrides *params.ChainConfig `json:"overrides,omitempty"`
|
||||
}
|
||||
|
||||
//go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go
|
||||
|
|
@ -101,10 +103,10 @@ func (s *StructLog) ErrorString() string {
|
|||
// Note that reference types are actual VM data structures; make copies
|
||||
// if you need to retain them beyond the current call.
|
||||
type Tracer interface {
|
||||
CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error
|
||||
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
|
||||
CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
|
||||
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
|
||||
CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
|
||||
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
|
||||
CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
|
||||
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error)
|
||||
}
|
||||
|
||||
// StructLogger is an EVM state logger and implements Tracer.
|
||||
|
|
@ -133,17 +135,19 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
|
|||
}
|
||||
|
||||
// CaptureStart implements the Tracer interface to initialize the tracing operation.
|
||||
func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
|
||||
return nil
|
||||
func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||
}
|
||||
|
||||
// CaptureState logs a new structured log message and pushes it out to the environment
|
||||
//
|
||||
// CaptureState also tracks SSTORE ops to track dirty values.
|
||||
func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
|
||||
func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
|
||||
memory := scope.Memory
|
||||
stack := scope.Stack
|
||||
contract := scope.Contract
|
||||
// check if already accumulated the specified number of logs
|
||||
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
|
||||
return errTraceLimitReached
|
||||
return
|
||||
}
|
||||
|
||||
// initialise new changed values storage container for this contract
|
||||
|
|
@ -184,17 +188,15 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
|
|||
log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), err}
|
||||
|
||||
l.logs = append(l.logs, log)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaptureFault implements the Tracer interface to trace an execution fault
|
||||
// while running an opcode.
|
||||
func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||
return nil
|
||||
func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
|
||||
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
|
||||
l.output = output
|
||||
l.err = err
|
||||
if l.cfg.Debug {
|
||||
|
|
@ -203,7 +205,6 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration
|
|||
fmt.Printf(" error: %v\n", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StructLogs returns the captured log entries.
|
||||
|
|
@ -257,3 +258,65 @@ func WriteLogs(writer io.Writer, logs []*types.Log) {
|
|||
fmt.Fprintln(writer)
|
||||
}
|
||||
}
|
||||
|
||||
type mdLogger struct {
|
||||
out io.Writer
|
||||
cfg *LogConfig
|
||||
}
|
||||
|
||||
// NewMarkdownLogger creates a logger which outputs information in a format adapted
|
||||
// for human readability, and is also a valid markdown table
|
||||
func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger {
|
||||
l := &mdLogger{writer, cfg}
|
||||
if l.cfg == nil {
|
||||
l.cfg = &LogConfig{}
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (t *mdLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||
if !create {
|
||||
fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
|
||||
from.String(), to.String(),
|
||||
input, gas, value)
|
||||
} else {
|
||||
fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
|
||||
from.String(), to.String(),
|
||||
input, gas, value)
|
||||
}
|
||||
|
||||
fmt.Fprintf(t.out, `
|
||||
| Pc | Op | Cost | Stack | RStack | Refund |
|
||||
|-------|-------------|------|-----------|-----------|---------|
|
||||
`)
|
||||
}
|
||||
|
||||
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
|
||||
func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
|
||||
stack := scope.Stack
|
||||
fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost)
|
||||
|
||||
if !t.cfg.DisableStack {
|
||||
// format stack
|
||||
var a []string
|
||||
for _, elem := range stack.data {
|
||||
a = append(a, fmt.Sprintf("%v", elem.String()))
|
||||
}
|
||||
b := fmt.Sprintf("[%v]", strings.Join(a, ","))
|
||||
fmt.Fprintf(t.out, "%10v |", b)
|
||||
}
|
||||
fmt.Fprintf(t.out, "%10v |", env.StateDB.GetRefund())
|
||||
fmt.Fprintln(t.out, "")
|
||||
if err != nil {
|
||||
fmt.Fprintf(t.out, "Error: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
|
||||
fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
|
||||
}
|
||||
|
||||
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) {
|
||||
fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n",
|
||||
output, gasUsed, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,12 +41,16 @@ func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger {
|
|||
return l
|
||||
}
|
||||
|
||||
func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
|
||||
return nil
|
||||
func (l *JSONLogger) CaptureStart(env *EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||
}
|
||||
|
||||
func (l *JSONLogger) CaptureFault(*EVM, uint64, OpCode, uint64, uint64, *ScopeContext, int, error) {}
|
||||
|
||||
// CaptureState outputs state information on the logger.
|
||||
func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||
func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
|
||||
memory := scope.Memory
|
||||
stack := scope.Stack
|
||||
|
||||
log := StructLog{
|
||||
Pc: pc,
|
||||
Op: op,
|
||||
|
|
@ -69,16 +73,11 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
|
|||
}
|
||||
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 l.CaptureState(env, pc, op, gas, cost, memory, stack, contract, depth, err)
|
||||
l.encoder.Encode(log)
|
||||
}
|
||||
|
||||
// CaptureEnd is triggered at end of execution.
|
||||
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
|
||||
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
|
||||
type endLog struct {
|
||||
Output string `json:"output"`
|
||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||
|
|
@ -89,5 +88,5 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration,
|
|||
if err != nil {
|
||||
errMsg = err.Error()
|
||||
}
|
||||
return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, errMsg})
|
||||
l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, errMsg})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,14 +52,17 @@ func TestStoreCapture(t *testing.T) {
|
|||
var (
|
||||
env = NewEVM(Context{}, &dummyStatedb{}, nil, params.TestChainConfig, Config{})
|
||||
logger = NewStructLogger(nil)
|
||||
mem = NewMemory()
|
||||
stack = newstack()
|
||||
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0)
|
||||
scope = &ScopeContext{
|
||||
Memory: NewMemory(),
|
||||
Stack: newstack(),
|
||||
Contract: contract,
|
||||
}
|
||||
)
|
||||
stack.push(uint256.NewInt(1))
|
||||
stack.push(new(uint256.Int))
|
||||
scope.Stack.push(uint256.NewInt(1))
|
||||
scope.Stack.push(new(uint256.Int))
|
||||
var index common.Hash
|
||||
logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, contract, 0, nil)
|
||||
logger.CaptureState(env, 0, SSTORE, 0, 0, scope, nil, 0, nil)
|
||||
if len(logger.changedValues[contract.Address()]) == 0 {
|
||||
t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()]))
|
||||
}
|
||||
|
|
|
|||
221
core/vm/operations_acl.go
Normal file
221
core/vm/operations_acl.go
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
// Copyright 2020 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 (
|
||||
"errors"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
)
|
||||
|
||||
const (
|
||||
ColdAccountAccessCostEIP2929 = uint64(2600) // COLD_ACCOUNT_ACCESS_COST
|
||||
ColdSloadCostEIP2929 = uint64(2100) // COLD_SLOAD_COST
|
||||
WarmStorageReadCostEIP2929 = uint64(100) // WARM_STORAGE_READ_COST
|
||||
)
|
||||
|
||||
// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929"
|
||||
//
|
||||
// When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys.
|
||||
// If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys.
|
||||
// Additionally, modify the parameters defined in EIP 2200 as follows:
|
||||
//
|
||||
// Parameter Old value New value
|
||||
// SLOAD_GAS 800 = WARM_STORAGE_READ_COST
|
||||
// SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST
|
||||
//
|
||||
// The other parameters defined in EIP 2200 are unchanged.
|
||||
// see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified
|
||||
func gasSStoreEIP2929(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 {
|
||||
return 0, errors.New("not enough gas for reentrancy sentry")
|
||||
}
|
||||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||
var (
|
||||
y, x = stack.Back(1), stack.peek()
|
||||
slot = common.Hash(x.Bytes32())
|
||||
current = evm.StateDB.GetState(contract.Address(), slot)
|
||||
cost = uint64(0)
|
||||
)
|
||||
// Check slot presence in the access list
|
||||
if addrPresent, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||||
cost = ColdSloadCostEIP2929
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||||
if !addrPresent {
|
||||
// Once we're done with YOLOv2 and schedule this for mainnet, might
|
||||
// be good to remove this panic here, which is just really a
|
||||
// canary to have during testing
|
||||
panic("impossible case: address was not present in access list during sstore op")
|
||||
}
|
||||
}
|
||||
value := common.Hash(y.Bytes32())
|
||||
|
||||
if current == value { // noop (1)
|
||||
// EIP 2200 original clause:
|
||||
// return params.SloadGasEIP2200, nil
|
||||
return cost + WarmStorageReadCostEIP2929, nil // SLOAD_GAS
|
||||
}
|
||||
original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
|
||||
if original == current {
|
||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||
return cost + params.SstoreSetGasEIP2200, nil
|
||||
}
|
||||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
|
||||
}
|
||||
// EIP-2200 original clause:
|
||||
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
|
||||
return cost + (params.SstoreResetGasEIP2200 - ColdSloadCostEIP2929), 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.SstoreClearsScheduleRefundEIP2200)
|
||||
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
|
||||
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
|
||||
}
|
||||
}
|
||||
if original == value {
|
||||
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
|
||||
// EIP 2200 Original clause:
|
||||
//evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
|
||||
evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - WarmStorageReadCostEIP2929)
|
||||
} else { // reset to original existing slot (2.2.2.2)
|
||||
// EIP 2200 Original clause:
|
||||
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
|
||||
// - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
|
||||
// - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
|
||||
// Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
|
||||
evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - ColdSloadCostEIP2929) - WarmStorageReadCostEIP2929)
|
||||
}
|
||||
}
|
||||
// EIP-2200 original clause:
|
||||
//return params.SloadGasEIP2200, nil // dirty update (2.2)
|
||||
return cost + WarmStorageReadCostEIP2929, nil // dirty update (2.2)
|
||||
}
|
||||
|
||||
// gasSLoadEIP2929 calculates dynamic gas for SLOAD according to EIP-2929
|
||||
// For SLOAD, if the (address, storage_key) pair (where address is the address of the contract
|
||||
// whose storage is being read) is not yet in accessed_storage_keys,
|
||||
// charge 2100 gas and add the pair to accessed_storage_keys.
|
||||
// If the pair is already in accessed_storage_keys, charge 100 gas.
|
||||
func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
loc := stack.peek()
|
||||
slot := common.Hash(loc.Bytes32())
|
||||
// Check slot presence in the access list
|
||||
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
// If he does afford it, we can skip checking the same thing later on, during execution
|
||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||||
return ColdSloadCostEIP2929, nil
|
||||
}
|
||||
return WarmStorageReadCostEIP2929, nil
|
||||
}
|
||||
|
||||
// gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929
|
||||
// EIP spec:
|
||||
// > If the target is not in accessed_addresses,
|
||||
// > charge COLD_ACCOUNT_ACCESS_COST gas, and add the address to accessed_addresses.
|
||||
// > Otherwise, charge WARM_STORAGE_READ_COST gas.
|
||||
func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
// memory expansion first (dynamic part of pre-2929 implementation)
|
||||
gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
addr := common.Address(stack.peek().Bytes20())
|
||||
// Check slot presence in the access list
|
||||
if !evm.StateDB.AddressInAccessList(addr) {
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
var overflow bool
|
||||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
||||
if gas, overflow = math.SafeAdd(gas, ColdAccountAccessCostEIP2929-WarmStorageReadCostEIP2929); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
// gasEip2929AccountCheck checks whether the first stack item (as address) is present in the access list.
|
||||
// If it is, this method returns '0', otherwise 'cold-warm' gas, presuming that the opcode using it
|
||||
// is also using 'warm' as constant factor.
|
||||
// This method is used by:
|
||||
// - extcodehash,
|
||||
// - extcodesize,
|
||||
// - (ext) balance
|
||||
func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
addr := common.Address(stack.peek().Bytes20())
|
||||
// Check slot presence in the access list
|
||||
if !evm.StateDB.AddressInAccessList(addr) {
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// The warm storage read cost is already charged as constantGas
|
||||
return ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
addr := common.Address(stack.Back(1).Bytes20())
|
||||
// Check slot presence in the access list
|
||||
if !evm.StateDB.AddressInAccessList(addr) {
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost
|
||||
if !contract.UseGas(ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929) {
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
// Now call the old calculator, which takes into account
|
||||
// - create new account
|
||||
// - transfer value
|
||||
// - memory expansion
|
||||
// - 63/64ths rule
|
||||
return oldCalculator(evm, contract, stack, mem, memorySize)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
gasCallEIP2929 = makeCallVariantGasCallEIP2929(gasCall)
|
||||
gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall)
|
||||
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall)
|
||||
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode)
|
||||
)
|
||||
|
||||
func gasSelfdestructEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var (
|
||||
gas uint64
|
||||
address = common.Address(stack.peek().Bytes20())
|
||||
)
|
||||
if !evm.StateDB.AddressInAccessList(address) {
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddAddressToAccessList(address)
|
||||
gas = ColdAccountAccessCostEIP2929
|
||||
}
|
||||
// if empty and transfers value
|
||||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
||||
gas += params.CreateBySelfdestructGas
|
||||
}
|
||||
if !evm.StateDB.HasSuicided(contract.Address()) {
|
||||
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
|
@ -17,12 +17,12 @@
|
|||
package runtime
|
||||
|
||||
import (
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"math"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
|
|
@ -107,6 +107,9 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
|||
vmenv = NewEnv(cfg)
|
||||
sender = vm.AccountRef(cfg.Origin)
|
||||
)
|
||||
if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsEIP1559 {
|
||||
cfg.State.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
|
||||
}
|
||||
cfg.State.CreateAccount(address)
|
||||
// set the receiver's (the executing contract) code for execution.
|
||||
cfg.State.SetCode(address, code)
|
||||
|
|
@ -137,7 +140,9 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
|
|||
vmenv = NewEnv(cfg)
|
||||
sender = vm.AccountRef(cfg.Origin)
|
||||
)
|
||||
|
||||
if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsEIP1559 {
|
||||
cfg.State.PrepareAccessList(cfg.Origin, nil, vm.ActivePrecompiles(rules), nil)
|
||||
}
|
||||
// Call the code with the given configuration.
|
||||
code, address, leftOverGas, err := vmenv.Create(
|
||||
sender,
|
||||
|
|
@ -159,6 +164,11 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
|
|||
vmenv := NewEnv(cfg)
|
||||
|
||||
sender := cfg.State.GetOrNewStateObject(cfg.Origin)
|
||||
statedb := cfg.State
|
||||
if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsEIP1559 {
|
||||
statedb.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
|
||||
}
|
||||
|
||||
// Call the code with the given configuration.
|
||||
ret, leftOverGas, err := vmenv.Call(
|
||||
sender,
|
||||
|
|
@ -167,6 +177,5 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
|
|||
cfg.GasLimit,
|
||||
cfg.Value,
|
||||
)
|
||||
|
||||
return ret, leftOverGas, err
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue