github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/core/multivm_processor.go (about)

     1  // +build sputnikvm
     2  
     3  package core
     4  
     5  import (
     6  	"math/big"
     7  
     8  	"github.com/ETCDEVTeam/sputnikvm-ffi/go/sputnikvm"
     9  	"github.com/ethereumproject/go-ethereum/common"
    10  	"github.com/ethereumproject/go-ethereum/core/state"
    11  	"github.com/ethereumproject/go-ethereum/core/types"
    12  	evm "github.com/ethereumproject/go-ethereum/core/vm"
    13  	"github.com/ethereumproject/go-ethereum/crypto"
    14  	"github.com/ethereumproject/go-ethereum/logger"
    15  	"github.com/ethereumproject/go-ethereum/logger/glog"
    16  )
    17  
    18  const SputnikVMExists = true
    19  
    20  // UseSputnikVM determines whether the VM will be Sputnik or Geth's native one.
    21  // Awkward though it is to use a string variable, go's -ldflags relies on it being a constant string in order to be settable via -X from the command line,
    22  // eg. -ldflags "-X core.UseSputnikVM=true".
    23  var UseSputnikVM string = "false"
    24  
    25  // Apply a transaction using the SputnikVM processor with the given
    26  // chain config and state. Note that we use the name of the chain
    27  // config to determine which hard fork to use so ClassicVM's gas table
    28  // would not be used.
    29  func ApplyMultiVmTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, totalUsedGas *big.Int) (*types.Receipt, evm.Logs, *big.Int, error) {
    30  	tx.SetSigner(config.GetSigner(header.Number))
    31  
    32  	from, err := tx.From()
    33  	if err != nil {
    34  		return nil, nil, nil, err
    35  	}
    36  	vmtx := sputnikvm.Transaction{
    37  		Caller:   from,
    38  		GasPrice: tx.GasPrice(),
    39  		GasLimit: tx.Gas(),
    40  		Address:  tx.To(),
    41  		Value:    tx.Value(),
    42  		Input:    tx.Data(),
    43  		Nonce:    new(big.Int).SetUint64(tx.Nonce()),
    44  	}
    45  	vmheader := sputnikvm.HeaderParams{
    46  		Beneficiary: header.Coinbase,
    47  		Timestamp:   header.Time.Uint64(),
    48  		Number:      header.Number,
    49  		Difficulty:  header.Difficulty,
    50  		GasLimit:    header.GasLimit,
    51  	}
    52  
    53  	currentNumber := header.Number
    54  	homesteadFork := config.ForkByName("Homestead")
    55  	eip150Fork := config.ForkByName("GasReprice")
    56  	eip160Fork := config.ForkByName("Diehard")
    57  
    58  	var vm *sputnikvm.VM
    59  	if state.StartingNonce == 0 {
    60  		if eip160Fork.Block != nil && currentNumber.Cmp(eip160Fork.Block) >= 0 {
    61  			vm = sputnikvm.NewEIP160(&vmtx, &vmheader)
    62  		} else if eip150Fork.Block != nil && currentNumber.Cmp(eip150Fork.Block) >= 0 {
    63  			vm = sputnikvm.NewEIP150(&vmtx, &vmheader)
    64  		} else if homesteadFork.Block != nil && currentNumber.Cmp(homesteadFork.Block) >= 0 {
    65  			vm = sputnikvm.NewHomestead(&vmtx, &vmheader)
    66  		} else {
    67  			vm = sputnikvm.NewFrontier(&vmtx, &vmheader)
    68  		}
    69  	} else if state.StartingNonce == 1048576 {
    70  		if eip160Fork.Block != nil && currentNumber.Cmp(eip160Fork.Block) >= 0 {
    71  			vm = sputnikvm.NewMordenEIP160(&vmtx, &vmheader)
    72  		} else if eip150Fork.Block != nil && currentNumber.Cmp(eip150Fork.Block) >= 0 {
    73  			vm = sputnikvm.NewMordenEIP150(&vmtx, &vmheader)
    74  		} else if homesteadFork.Block != nil && currentNumber.Cmp(homesteadFork.Block) >= 0 {
    75  			vm = sputnikvm.NewMordenHomestead(&vmtx, &vmheader)
    76  		} else {
    77  			vm = sputnikvm.NewMordenFrontier(&vmtx, &vmheader)
    78  		}
    79  	} else {
    80  		sputnikvm.SetCustomInitialNonce(big.NewInt(int64(state.StartingNonce)))
    81  		if eip160Fork.Block != nil && currentNumber.Cmp(eip160Fork.Block) >= 0 {
    82  			vm = sputnikvm.NewCustomEIP160(&vmtx, &vmheader)
    83  		} else if eip150Fork.Block != nil && currentNumber.Cmp(eip150Fork.Block) >= 0 {
    84  			vm = sputnikvm.NewCustomEIP150(&vmtx, &vmheader)
    85  		} else if homesteadFork.Block != nil && currentNumber.Cmp(homesteadFork.Block) >= 0 {
    86  			vm = sputnikvm.NewCustomHomestead(&vmtx, &vmheader)
    87  		} else {
    88  			vm = sputnikvm.NewCustomFrontier(&vmtx, &vmheader)
    89  		}
    90  	}
    91  
    92  Loop:
    93  	for {
    94  		ret := vm.Fire()
    95  		switch ret.Typ() {
    96  		case sputnikvm.RequireNone:
    97  			break Loop
    98  		case sputnikvm.RequireAccount:
    99  			address := ret.Address()
   100  			if statedb.Exist(address) {
   101  				vm.CommitAccount(address, new(big.Int).SetUint64(statedb.GetNonce(address)),
   102  					statedb.GetBalance(address), statedb.GetCode(address))
   103  				break
   104  			}
   105  			vm.CommitNonexist(address)
   106  		case sputnikvm.RequireAccountCode:
   107  			address := ret.Address()
   108  			if statedb.Exist(address) {
   109  				vm.CommitAccountCode(address, statedb.GetCode(address))
   110  				break
   111  			}
   112  			vm.CommitNonexist(address)
   113  		case sputnikvm.RequireAccountStorage:
   114  			address := ret.Address()
   115  			key := common.BigToHash(ret.StorageKey())
   116  			if statedb.Exist(address) {
   117  				value := statedb.GetState(address, key).Big()
   118  				sKey := ret.StorageKey()
   119  				vm.CommitAccountStorage(address, sKey, value)
   120  				break
   121  			}
   122  			vm.CommitNonexist(address)
   123  		case sputnikvm.RequireBlockhash:
   124  			number := ret.BlockNumber()
   125  			hash := common.Hash{}
   126  			if block := bc.GetBlockByNumber(number.Uint64()); block != nil && block.Number().Cmp(number) == 0 {
   127  				hash = block.Hash()
   128  			}
   129  			vm.CommitBlockhash(number, hash)
   130  		}
   131  	}
   132  
   133  	// VM execution is finished at this point. We apply changes to the statedb.
   134  
   135  	for _, account := range vm.AccountChanges() {
   136  		switch account.Typ() {
   137  		case sputnikvm.AccountChangeIncreaseBalance:
   138  			address := account.Address()
   139  			amount := account.ChangedAmount()
   140  			statedb.AddBalance(address, amount)
   141  		case sputnikvm.AccountChangeDecreaseBalance:
   142  			address := account.Address()
   143  			amount := account.ChangedAmount()
   144  			balance := new(big.Int).Sub(statedb.GetBalance(address), amount)
   145  			statedb.SetBalance(address, balance)
   146  		case sputnikvm.AccountChangeRemoved:
   147  			address := account.Address()
   148  			statedb.Suicide(address)
   149  		case sputnikvm.AccountChangeFull:
   150  			address := account.Address()
   151  			code := account.Code()
   152  			nonce := account.Nonce()
   153  			balance := account.Balance()
   154  			statedb.SetBalance(address, balance)
   155  			statedb.SetNonce(address, nonce.Uint64())
   156  			statedb.SetCode(address, code)
   157  			for _, item := range account.ChangedStorage() {
   158  				statedb.SetState(address, common.BigToHash(item.Key), common.BigToHash(item.Value))
   159  			}
   160  		case sputnikvm.AccountChangeCreate:
   161  			address := account.Address()
   162  			code := account.Code()
   163  			nonce := account.Nonce()
   164  			balance := account.Balance()
   165  			statedb.SetBalance(address, balance)
   166  			statedb.SetNonce(address, nonce.Uint64())
   167  			statedb.SetCode(address, code)
   168  			for _, item := range account.Storage() {
   169  				statedb.SetState(address, common.BigToHash(item.Key), common.BigToHash(item.Value))
   170  			}
   171  		default:
   172  			panic("unreachable")
   173  		}
   174  	}
   175  	for _, log := range vm.Logs() {
   176  		statelog := evm.NewLog(log.Address, log.Topics, log.Data, header.Number.Uint64())
   177  		statedb.AddLog(*statelog)
   178  	}
   179  	usedGas := vm.UsedGas()
   180  	totalUsedGas.Add(totalUsedGas, usedGas)
   181  
   182  	receipt := types.NewReceipt(statedb.IntermediateRoot(false).Bytes(), totalUsedGas)
   183  	receipt.TxHash = tx.Hash()
   184  	receipt.GasUsed = new(big.Int).Set(totalUsedGas)
   185  	if vm.Failed() {
   186  		receipt.Status = types.TxFailure
   187  	} else {
   188  		receipt.Status = types.TxSuccess
   189  	}
   190  	if MessageCreatesContract(tx) {
   191  		receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce())
   192  	}
   193  
   194  	logs := statedb.GetLogs(tx.Hash())
   195  	receipt.Logs = logs
   196  	receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
   197  
   198  	glog.V(logger.Debug).Infoln(receipt)
   199  
   200  	vm.Free()
   201  	return receipt, logs, totalUsedGas, nil
   202  }