Skip to content

Commit

Permalink
Merge pull request #8 from quilt/aa-tx-execution
Browse files Browse the repository at this point in the history
Add AA Transaction Execution Logic
  • Loading branch information
adietrichs authored Jun 3, 2020
2 parents 92e2040 + f89c7ea commit b4f58f8
Show file tree
Hide file tree
Showing 12 changed files with 200 additions and 27 deletions.
8 changes: 8 additions & 0 deletions accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,14 @@ 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) IsAA() bool { return m.CallMsg.IsAA }
func (m callmsg) Sponsor() common.Address {
if !m.CallMsg.IsAA {
return m.CallMsg.From
} else {
return *m.CallMsg.To
}
}

// filterBackend implements filters.Backend to support filtering for logs without
// taking bloom-bits acceleration structures into account.
Expand Down
19 changes: 19 additions & 0 deletions common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,15 @@ func (h UnprefixedHash) MarshalText() ([]byte, error) {
// Address represents the 20 byte address of an Ethereum account.
type Address [AddressLength]byte

// NewEntryPointAddress returns an ENTRY_POINT Address.
func NewEntryPointAddress() Address {
var a Address
for i := range a {
a[i] = 0xFF
}
return a
}

// BytesToAddress returns Address with value b.
// If b is larger than len(h), b will be cropped from the left.
func BytesToAddress(b []byte) Address {
Expand All @@ -200,6 +209,16 @@ func IsHexAddress(s string) bool {
return len(s) == 2*AddressLength && isHex(s)
}

// IsEntryPointAddress returns true if the Address is the ENTRY_POINT Address.
func (a Address) IsEntryPoint() bool {
for i := range a {
if a[i] != 0xFF {
return false
}
}
return true
}

// Bytes gets the string representation of the underlying address.
func (a Address) Bytes() []byte { return a[:] }

Expand Down
6 changes: 6 additions & 0 deletions core/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,10 @@ var (
// ErrIntrinsicGas is returned if the transaction is specified to use less gas
// than required to start the invocation.
ErrIntrinsicGas = errors.New("intrinsic gas too low")

ErrInvalidAATransaction = errors.New("invalid account abstraction transaction")

ErrInvalidAAPrefix = errors.New("invalid account abstraction prefix")

ErrNoPaygas = errors.New("account abstraction transaction did not call PAYGAS")
)
55 changes: 48 additions & 7 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ import (
"github.com/ethereum/go-ethereum/params"
)

var aaPrefix = [...]byte{
0x33, 0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x14, 0x60, 0x24, 0x57, 0x36, 0x60, 0x1f, 0x57, 0x00, 0x5b, 0x60, 0x00, 0x80, 0xfd, 0x5b,
}

/*
The State Transitioning Model
Expand Down Expand Up @@ -66,6 +71,9 @@ type Message interface {
Nonce() uint64
CheckNonce() bool
Data() []byte

IsAA() bool
Sponsor() common.Address
}

// ExecutionResult includes all output after executing given evm
Expand Down Expand Up @@ -173,17 +181,23 @@ func (st *StateTransition) to() common.Address {
}

func (st *StateTransition) buyGas() error {
mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice)
if st.state.GetBalance(st.msg.From()).Cmp(mgval) < 0 {
return ErrInsufficientFunds
var mgval *big.Int
if !st.msg.IsAA() {
mgval = new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice)
if st.state.GetBalance(st.msg.Sponsor()).Cmp(mgval) < 0 {
return ErrInsufficientFunds
}
}
if err := st.gp.SubGas(st.msg.Gas()); err != nil {
return err
}
st.gas += st.msg.Gas()

st.initialGas = st.msg.Gas()
st.state.SubBalance(st.msg.From(), mgval)
if !st.msg.IsAA() {
st.state.SubBalance(st.msg.Sponsor(), mgval)
}

return nil
}

Expand Down Expand Up @@ -224,6 +238,25 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
// 5. there is no overflow when calculating intrinsic gas
// 6. caller has enough balance to cover asset transfer for **topmost** call

if st.msg.IsAA() {
if st.msg.Nonce() != 0 || st.msg.GasPrice().Sign() != 0 || st.msg.Value().Sign() != 0 {
return nil, ErrInvalidAATransaction
}

code := st.evm.StateDB.GetCode(st.to())
if code == nil || len(code) < len(aaPrefix) {
return nil, ErrInvalidAAPrefix
}
for i := range aaPrefix {
if code[i] != aaPrefix[i] {
return nil, ErrInvalidAAPrefix
}
}
if st.evm.PaygasMode() == vm.PaygasNoOp {
st.evm.SetPaygasMode(vm.PaygasContinue)
}
}

// Check clauses 1-3, buy gas if everything is correct
if err := st.preCheck(); err != nil {
return nil, err
Expand Down Expand Up @@ -255,9 +288,17 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
if contractCreation {
ret, _, st.gas, vmerr = st.evm.Create(sender, st.data, st.gas, st.value)
} else {
// Increment the nonce for the next transaction
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
if !msg.IsAA() {
// Increment the nonce for the next transaction
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
}
ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value)
if msg.IsAA() && st.evm.PaygasMode() == vm.PaygasContinue {
return nil, ErrNoPaygas
}
}
if st.msg.IsAA() {
st.gasPrice = st.evm.PaygasPrice()
}
st.refundGas()
st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
Expand All @@ -279,7 +320,7 @@ func (st *StateTransition) refundGas() {

// Return ETH for remaining gas, exchanged at the original rate.
remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice)
st.state.AddBalance(st.msg.From(), remaining)
st.state.AddBalance(st.msg.Sponsor(), remaining)

// Also return remaining gas to the block gas counter so it is
// available for the next transaction.
Expand Down
50 changes: 50 additions & 0 deletions core/transaction_validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package core

import (
"errors"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
)

var (
ErrIncorrectAAConfig = errors.New("incorrect AA config for EVM")
)

func Validate(tx *types.Transaction, s types.Signer, evm *vm.EVM, gasLimit uint64) error {
if evm.PaygasMode() != vm.PaygasHalt || evm.PaygasPrice().Sign() != 0 {
return ErrIncorrectAAConfig
}
msg, err := tx.AsMessage(s)
if err != nil {
return err
}
if gasLimit > msg.Gas() {
gasLimit = msg.Gas()
}
gp := new(GasPool).AddGas(gasLimit)
result, err := ApplyMessage(evm, msg, gp)
if err != nil {
return err
}
if result.Err != nil {
return result.Err
}
tx.SetAAGasPrice(evm.PaygasPrice())
return nil
}
40 changes: 30 additions & 10 deletions core/types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ var (
type Transaction struct {
data txdata
// caches
hash atomic.Value
size atomic.Value
from atomic.Value
hash atomic.Value
size atomic.Value
from atomic.Value
price atomic.Value
}

type txdata struct {
Expand Down Expand Up @@ -212,24 +213,30 @@ func (tx *Transaction) Size() common.StorageSize {
return common.StorageSize(c)
}

// IsAA returns the result of a basic AA signature check.
func (tx *Transaction) IsAA() bool {
return tx.data.R.Sign() == 0 && tx.data.S.Sign() == 0
}

// 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) (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,
checkNonce: true,
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,
}

var err error
msg.from, err = Sender(s, tx)
msg.isAA = msg.from.IsEntryPoint()
msg.checkNonce = !msg.isAA
return msg, err
}

Expand Down Expand Up @@ -258,6 +265,10 @@ func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) {
return tx.data.V, tx.data.R, tx.data.S
}

func (tx *Transaction) SetAAGasPrice(price *big.Int) {
tx.price.Store(price)
}

// Transactions is a Transaction slice type for basic sorting.
type Transactions []*Transaction

Expand Down Expand Up @@ -394,6 +405,7 @@ type Message struct {
gasPrice *big.Int
data []byte
checkNonce bool
isAA bool
}

func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool) Message {
Expand All @@ -417,3 +429,11 @@ 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) IsAA() bool { return m.isAA }
func (m Message) Sponsor() common.Address {
if !m.isAA {
return m.from
} else {
return *m.to
}
}
3 changes: 3 additions & 0 deletions core/types/transaction_signing.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) {
return common.Address{}, ErrInvalidChainId
}
V := new(big.Int).Sub(tx.data.V, s.chainIdMul)
if tx.data.R.Sign() == 0 && tx.data.S.Sign() == 0 && V.BitLen() <= 8 && V.Uint64() == 35 {
return common.NewEntryPointAddress(), nil
}
V.Sub(V, big8)
return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true)
}
Expand Down
1 change: 1 addition & 0 deletions core/vm/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ var (
ErrWriteProtection = errors.New("write protection")
ErrReturnDataOutOfBounds = errors.New("return data out of bounds")
ErrGasUintOverflow = errors.New("gas uint64 overflow")
ErrPaygasInsufficientFunds = errors.New("insufficient funds for gas * price + value")
)

// ErrStackUnderflow wraps an evm error when the items on the stack less
Expand Down
17 changes: 12 additions & 5 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
}(evm.interpreter)
evm.interpreter = interpreter
}
return interpreter.Run(contract, input, readOnly)
interpreter.SetAAConfig(evm.vmConfig.PaygasMode, evm.vmConfig.paygasPrice)
res, err := interpreter.Run(contract, input, readOnly)
evm.vmConfig.PaygasMode, evm.vmConfig.paygasPrice = interpreter.GetAAConfig()
return res, err
}
}
return nil, errors.New("no compatible interpreter")
Expand Down Expand Up @@ -285,7 +288,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
evm.pushSnapshot()

var (
to = AccountRef(caller.Address())
to = AccountRef(caller.Address())
)
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
Expand All @@ -294,7 +297,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,

ret, err = run(evm, contract, input, false)

snapshot := evm.popSnapshot();
snapshot := evm.popSnapshot()

if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
Expand Down Expand Up @@ -332,7 +335,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
evm.pushSnapshot()

var (
to = AccountRef(caller.Address())
to = AccountRef(caller.Address())
)
// Initialise a new contract and make initialise the delegate values
contract := NewContract(caller, to, nil, gas).AsDelegate()
Expand Down Expand Up @@ -364,7 +367,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
return nil, gas, ErrDepth
}
var (
to = AccountRef(addr)
to = AccountRef(addr)
)

evm.pushSnapshot()
Expand Down Expand Up @@ -506,3 +509,7 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *

// ChainConfig returns the environment's chain configuration
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }

func (evm *EVM) PaygasMode() PaygasMode { return evm.vmConfig.PaygasMode }
func (evm *EVM) SetPaygasMode(paygasMode PaygasMode) { evm.vmConfig.PaygasMode = paygasMode }
func (evm *EVM) PaygasPrice() *big.Int { return evm.vmConfig.paygasPrice }
7 changes: 6 additions & 1 deletion core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,12 @@ func opPaygas(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
if interpreter.paygasMode == PaygasNoOp {
interpreter.intPool.put(gasprice)
} else {
// TODO: Check the computed value against the GasPrice set in the tx
mgval := new(big.Int).Mul(new(big.Int).SetUint64(interpreter.evm.GasLimit), gasprice)
if interpreter.evm.StateDB.GetBalance(callContext.contract.Address()).Cmp(mgval) < 0 {
return nil, ErrPaygasInsufficientFunds
}
interpreter.evm.StateDB.SubBalance(callContext.contract.Address(), mgval)

interpreter.evm.snapshots[len(interpreter.evm.snapshots)-1] = interpreter.evm.StateDB.Snapshot()
interpreter.paygasMode = PaygasNoOp
interpreter.paygasPrice = gasprice
Expand Down
Loading

0 comments on commit b4f58f8

Please sign in to comment.