Skip to content

Commit

Permalink
Merge pull request #39 from quilt/eip-3074-authorize
Browse files Browse the repository at this point in the history
EIP-3074: Update to two-opcode spec
  • Loading branch information
adietrichs authored Mar 13, 2021
2 parents dac098c + 5144a99 commit ebaefcc
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 60 deletions.
116 changes: 63 additions & 53 deletions core/vm/eips.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,36 +149,35 @@ func enable2929(jt *JumpTable) {
}

func enable3074(jt *JumpTable) {
jt[CALLFROM] = &operation{
execute: opCallFrom,
constantGas: WarmStorageReadCostEIP2929 + params.EcrecoverGas,
dynamicGas: gasCallEIP2929,
minStack: minStack(12, 2),
maxStack: maxStack(12, 2),
memorySize: memoryCall,
jt[AUTH] = &operation{
execute: opAuth,
constantGas: params.EcrecoverGas,
minStack: minStack(4, 1),
maxStack: maxStack(4, 1),
}

jt[AUTHCALL] = &operation{
execute: opAuthCall,
constantGas: jt[CALL].constantGas,
dynamicGas: jt[CALL].dynamicGas,
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
memorySize: jt[CALL].memorySize,
returns: true,
}
}

func opCallFrom(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
func opAuth(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
stack := callContext.stack
// Pop gas. The actual gas in interpreter.evm.callGasTemp.
// We can use this as a temporary value
temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
commit, sponsee, sigV, sigR, sigS := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()

valid, success := false, false
var ret []byte = nil
commit, sigYParity, sigR, sigS := stack.pop(), stack.pop(), stack.pop(), stack.pop()

r := sigR.ToBig()
s := sigS.ToBig()
v := uint8(sigV[0]) - 27
yParity := uint8(sigYParity[0])

callContext.authorizedAccount = nil

// tighter sig s values input homestead only apply to tx sigs
if val, of := sigV.Uint64WithOverflow(); !of && val <= 0xff && crypto.ValidateSignatureValues(v, r, s, false) {
if val, of := sigYParity.Uint64WithOverflow(); !of && val <= 0xff && crypto.ValidateSignatureValues(yParity, r, s, true) {
input := make([]byte, 65)
input[0] = 0x03
copy(input[13:33], callContext.contract.Address().Bytes())
Expand All @@ -188,54 +187,65 @@ func opCallFrom(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
sig := make([]byte, 65)
sigR.WriteToSlice(sig[0:32])
sigS.WriteToSlice(sig[32:64])
sig[64] = v
sig[64] = yParity
// v needs to be at the end for libsecp256k1
pubKey, err := crypto.Ecrecover(hash[:], sig)
// make sure the public key is a valid one
// the first byte of pubkey is bitcoin heritage
if err == nil {
from := common.BytesToAddress(crypto.Keccak256(pubKey[1:])[12:])
if sponsee.IsZero() || sponsee.Bytes20() == from {
valid = true

toAddr := common.Address(addr.Bytes20())
// Get the arguments from the memory.
args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))

var bigVal = big0
//TODO: use uint256.Int instead of converting with toBig()
// By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls),
// but it would make more sense to extend the usage of uint256.Int
if !value.IsZero() {
gas += params.CallStipend
bigVal = value.ToBig()
}

ret, returnGas, err := interpreter.evm.Call(callContext.contract, from, toAddr, args, gas, bigVal)

success = err == nil
if success || err == ErrExecutionReverted {
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
callContext.contract.Gas += returnGas
if from != interpreter.evm.Origin {
callContext.authorizedAccount = &from
}
}
}

if valid {
addr.SetOne()
if callContext.authorizedAccount != nil {
commit.SetBytes20(callContext.authorizedAccount.Bytes())
} else {
addr.Clear()
callContext.contract.Gas += gas
commit.Clear()
}
stack.push(&addr)
stack.push(&commit)
return nil, nil
}

if success {
temp.SetOne()
} else {
func opAuthCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
if callContext.authorizedAccount == nil {
return nil, ErrNoAuthorizedAccount
}

stack := callContext.stack
// Pop gas. The actual gas in interpreter.evm.callGasTemp.
// We can use this as a temporary value
temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
addr, value, inOffset, inSize, retOffset, retSize := 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()))

var bigVal = big0
//TODO: use uint256.Int instead of converting with toBig()
// By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls),
// but it would make more sense to extend the usage of uint256.Int
if !value.IsZero() {
gas += params.CallStipend
bigVal = value.ToBig()
}

ret, returnGas, err := interpreter.evm.Call(callContext.contract, *callContext.authorizedAccount, toAddr, args, gas, bigVal)

if err != nil {
temp.Clear()
} else {
temp.SetOne()
}
stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
callContext.contract.Gas += returnGas

return ret, nil
}
1 change: 1 addition & 0 deletions core/vm/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ var (
ErrGasUintOverflow = errors.New("gas uint64 overflow")
ErrInvalidRetsub = errors.New("invalid retsub")
ErrReturnStackExceeded = errors.New("return stack limit reached")
ErrNoAuthorizedAccount = errors.New("authorized account not set")
)

// ErrStackUnderflow wraps an evm error when the items on the stack less
Expand Down
7 changes: 4 additions & 3 deletions core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,10 @@ type Interpreter interface {
// callCtx 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
memory *Memory
stack *Stack
contract *Contract
authorizedAccount *common.Address
}

// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
Expand Down
9 changes: 6 additions & 3 deletions core/vm/opcodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,8 @@ const (
RETURN
DELEGATECALL
CREATE2
CALLFROM OpCode = 0xf9
AUTH
AUTHCALL
STATICCALL OpCode = 0xfa
REVERT OpCode = 0xfd
SELFDESTRUCT OpCode = 0xff
Expand Down Expand Up @@ -374,7 +375,8 @@ var opCodeToString = map[OpCode]string{
// 0xf0 range.
CREATE: "CREATE",
CALL: "CALL",
CALLFROM: "CALLFROM",
AUTH: "AUTH",
AUTHCALL: "AUTHCALL",
RETURN: "RETURN",
CALLCODE: "CALLCODE",
DELEGATECALL: "DELEGATECALL",
Expand Down Expand Up @@ -535,7 +537,8 @@ var stringToOp = map[string]OpCode{
"CREATE": CREATE,
"CREATE2": CREATE2,
"CALL": CALL,
"CALLFROM": CALLFROM,
"AUTH": AUTH,
"AUTHCALL": AUTHCALL,
"RETURN": RETURN,
"CALLCODE": CALLCODE,
"REVERT": REVERT,
Expand Down
3 changes: 2 additions & 1 deletion params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ func (c *ChainConfig) String() string {
default:
engine = "unknown"
}
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, YOLO v3: %v, Engine: %v}",
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, YOLO v3: %v, Quilt: %v, Engine: %v}",
c.ChainID,
c.HomesteadBlock,
c.DAOForkBlock,
Expand All @@ -380,6 +380,7 @@ func (c *ChainConfig) String() string {
c.MuirGlacierBlock,
c.BerlinBlock,
c.YoloV3Block,
c.QuiltBlock,
engine,
)
}
Expand Down

0 comments on commit ebaefcc

Please sign in to comment.