Skip to content

Commit

Permalink
Read the Tobin tax amount from the Reserve smart contract and use in …
Browse files Browse the repository at this point in the history
…TobinTransfer function (ethereum#138)

* Create TobinTransfer function

* Import log and hexutil

* Make sure to charge gas everywhere, create function selector for getTobinTax, and add/improve comments

* Still complete transfer if getTobinTax doesn't work

* Complete transfer if TobinTransfer amount is zero

* Refactor the way precompiled contracts are run to properly charge gas when calling TobinTransfer in contracts.go

* Refactor hasEnoughGas function

* Use debitRequiredGas function when running precompiled contracts and refactor output order of Run functions to (ret, gas, err)

* Change caller in TobinTransfer to sender, check for call error, and refactor minor things

* Add comments and logs

* Simplify logic for TobinTransfer and charge gas in RunPrecompiledContract

* Change tobin transfer error log
  • Loading branch information
yasmeen authored Mar 27, 2019
1 parent bcf3d8a commit 45396b5
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 57 deletions.
Binary file removed build/bin/geth.aar
Binary file not shown.
7 changes: 1 addition & 6 deletions core/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
)

// ChainContext supports retrieving headers and consensus parameters from the
Expand Down Expand Up @@ -122,10 +121,6 @@ func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool {

// Transfer subtracts amount from sender and adds amount to recipient using the given Db
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
// TODO(yasmeen): Read the tobin tax amount from the reserve smart contract
tobinTax := big.NewInt(0)

db.SubBalance(sender, amount)
db.AddBalance(recipient, new(big.Int).Sub(amount, tobinTax))
db.AddBalance(params.ReserveAddress, tobinTax)
db.AddBalance(recipient, amount)
}
154 changes: 105 additions & 49 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ import (
// requires a deterministic gas count based on the input size of the Run method of the
// contract.
type PrecompiledContract interface {
RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
Run(input []byte, caller common.Address, evm *EVM) ([]byte, error) // Run runs the precompiled contract
RequiredGas(input []byte) uint64 // RequiredGas calculates the contract gas use
Run(input []byte, caller common.Address, evm *EVM, gas uint64) ([]byte, uint64, error) // Run runs the precompiled contract
}

// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum
Expand Down Expand Up @@ -75,12 +75,17 @@ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{

// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract, evm *EVM) (ret []byte, err error) {
gas := p.RequiredGas(input)
if contract.UseGas(gas) {
return p.Run(input, contract.CallerAddress, evm)
ret, gas, err := p.Run(input, contract.CallerAddress, evm, contract.Gas)
contract.UseGas(contract.Gas - gas)
return ret, err
}

func debitRequiredGas(p PrecompiledContract, input []byte, gas uint64) (uint64, error) {
requiredGas := p.RequiredGas(input)
if requiredGas > gas {
return gas, ErrOutOfGas
}
log.Debug("RunPrecompiledContract out of gas")
return nil, ErrOutOfGas
return gas - requiredGas, nil
}

// ECRECOVER implemented as a native contract.
Expand All @@ -90,7 +95,12 @@ func (c *ecrecover) RequiredGas(input []byte) uint64 {
return params.EcrecoverGas
}

func (c *ecrecover) Run(input []byte, caller common.Address, evm *EVM) ([]byte, error) {
func (c *ecrecover) Run(input []byte, caller common.Address, evm *EVM, gas uint64) ([]byte, uint64, error) {
gas, err := debitRequiredGas(c, input, gas)
if err != nil {
return nil, gas, err
}

const ecRecoverInputLength = 128

input = common.RightPadBytes(input, ecRecoverInputLength)
Expand All @@ -103,17 +113,17 @@ func (c *ecrecover) Run(input []byte, caller common.Address, evm *EVM) ([]byte,

// tighter sig s values input homestead only apply to tx sigs
if !allZero(input[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) {
return nil, nil
return nil, gas, nil
}
// v needs to be at the end for libsecp256k1
pubKey, err := crypto.Ecrecover(input[:32], append(input[64:128], v))
// make sure the public key is a valid one
if err != nil {
return nil, nil
return nil, gas, nil
}

// the first byte of pubkey is bitcoin heritage
return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32), nil
return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32), gas, nil
}

// SHA256 implemented as a native contract.
Expand All @@ -126,9 +136,14 @@ type sha256hash struct{}
func (c *sha256hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas
}
func (c *sha256hash) Run(input []byte, caller common.Address, evm *EVM) ([]byte, error) {
func (c *sha256hash) Run(input []byte, caller common.Address, evm *EVM, gas uint64) ([]byte, uint64, error) {
gas, err := debitRequiredGas(c, input, gas)
if err != nil {
return nil, gas, err
}

h := sha256.Sum256(input)
return h[:], nil
return h[:], gas, nil
}

// RIPEMD160 implemented as a native contract.
Expand All @@ -141,10 +156,15 @@ type ripemd160hash struct{}
func (c *ripemd160hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas
}
func (c *ripemd160hash) Run(input []byte, caller common.Address, evm *EVM) ([]byte, error) {
func (c *ripemd160hash) Run(input []byte, caller common.Address, evm *EVM, gas uint64) ([]byte, uint64, error) {
gas, err := debitRequiredGas(c, input, gas)
if err != nil {
return nil, gas, err
}

ripemd := ripemd160.New()
ripemd.Write(input)
return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
return common.LeftPadBytes(ripemd.Sum(nil), 32), gas, nil
}

// data copy implemented as a native contract.
Expand All @@ -157,8 +177,13 @@ type dataCopy struct{}
func (c *dataCopy) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas
}
func (c *dataCopy) Run(in []byte, caller common.Address, evm *EVM) ([]byte, error) {
return in, nil
func (c *dataCopy) Run(input []byte, caller common.Address, evm *EVM, gas uint64) ([]byte, uint64, error) {
gas, err := debitRequiredGas(c, input, gas)
if err != nil {
return nil, gas, err
}

return input, gas, nil
}

// bigModExp implements a native big integer exponential modular operation.
Expand Down Expand Up @@ -238,7 +263,12 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 {
return gas.Uint64()
}

func (c *bigModExp) Run(input []byte, caller common.Address, evm *EVM) ([]byte, error) {
func (c *bigModExp) Run(input []byte, caller common.Address, evm *EVM, gas uint64) ([]byte, uint64, error) {
gas, err := debitRequiredGas(c, input, gas)
if err != nil {
return nil, gas, err
}

var (
baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64()
expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64()
Expand All @@ -251,7 +281,7 @@ func (c *bigModExp) Run(input []byte, caller common.Address, evm *EVM) ([]byte,
}
// Handle a special case when both the base and mod length is zero
if baseLen == 0 && modLen == 0 {
return []byte{}, nil
return []byte{}, gas, nil
}
// Retrieve the operands and execute the exponentiation
var (
Expand All @@ -261,9 +291,9 @@ func (c *bigModExp) Run(input []byte, caller common.Address, evm *EVM) ([]byte,
)
if mod.BitLen() == 0 {
// Modulo 0 is undefined, return zero
return common.LeftPadBytes([]byte{}, int(modLen)), nil
return common.LeftPadBytes([]byte{}, int(modLen)), gas, nil
}
return common.LeftPadBytes(base.Exp(base, exp, mod).Bytes(), int(modLen)), nil
return common.LeftPadBytes(base.Exp(base, exp, mod).Bytes(), int(modLen)), gas, nil
}

// newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point,
Expand Down Expand Up @@ -294,18 +324,23 @@ func (c *bn256Add) RequiredGas(input []byte) uint64 {
return params.Bn256AddGas
}

func (c *bn256Add) Run(input []byte, caller common.Address, evm *EVM) ([]byte, error) {
func (c *bn256Add) Run(input []byte, caller common.Address, evm *EVM, gas uint64) ([]byte, uint64, error) {
gas, err := debitRequiredGas(c, input, gas)
if err != nil {
return nil, gas, err
}

x, err := newCurvePoint(getData(input, 0, 64))
if err != nil {
return nil, err
return nil, gas, err
}
y, err := newCurvePoint(getData(input, 64, 64))
if err != nil {
return nil, err
return nil, gas, err
}
res := new(bn256.G1)
res.Add(x, y)
return res.Marshal(), nil
return res.Marshal(), gas, nil
}

// bn256ScalarMul implements a native elliptic curve scalar multiplication.
Expand All @@ -316,14 +351,19 @@ func (c *bn256ScalarMul) RequiredGas(input []byte) uint64 {
return params.Bn256ScalarMulGas
}

func (c *bn256ScalarMul) Run(input []byte, caller common.Address, evm *EVM) ([]byte, error) {
func (c *bn256ScalarMul) Run(input []byte, caller common.Address, evm *EVM, gas uint64) ([]byte, uint64, error) {
gas, err := debitRequiredGas(c, input, gas)
if err != nil {
return nil, gas, err
}

p, err := newCurvePoint(getData(input, 0, 64))
if err != nil {
return nil, err
return nil, gas, err
}
res := new(bn256.G1)
res.ScalarMult(p, new(big.Int).SetBytes(getData(input, 64, 32)))
return res.Marshal(), nil
return res.Marshal(), gas, nil
}

var (
Expand All @@ -345,10 +385,15 @@ func (c *bn256Pairing) RequiredGas(input []byte) uint64 {
return params.Bn256PairingBaseGas + uint64(len(input)/192)*params.Bn256PairingPerPointGas
}

func (c *bn256Pairing) Run(input []byte, caller common.Address, evm *EVM) ([]byte, error) {
func (c *bn256Pairing) Run(input []byte, caller common.Address, evm *EVM, gas uint64) ([]byte, uint64, error) {
gas, err := debitRequiredGas(c, input, gas)
if err != nil {
return nil, gas, err
}

// Handle some corner cases cheaply
if len(input)%192 > 0 {
return nil, errBadPairingInput
return nil, gas, errBadPairingInput
}
// Convert the input into a set of coordinates
var (
Expand All @@ -358,20 +403,20 @@ func (c *bn256Pairing) Run(input []byte, caller common.Address, evm *EVM) ([]byt
for i := 0; i < len(input); i += 192 {
c, err := newCurvePoint(input[i : i+64])
if err != nil {
return nil, err
return nil, gas, err
}
t, err := newTwistPoint(input[i+64 : i+192])
if err != nil {
return nil, err
return nil, gas, err
}
cs = append(cs, c)
ts = append(ts, t)
}
// Execute the pairing checks and return the results
if bn256.PairingCheck(cs, ts) {
return true32Byte, nil
return true32Byte, gas, nil
}
return false32Byte, nil
return false32Byte, gas, nil
}

// Requesting verification in the Celo address based encryption protocol is implemented as a
Expand All @@ -384,16 +429,21 @@ func (c *requestVerification) RequiredGas(input []byte) uint64 {
}

// Ensures that the input is parsable as a VerificationRequest.
func (c *requestVerification) Run(input []byte, caller common.Address, evm *EVM) ([]byte, error) {
func (c *requestVerification) Run(input []byte, caller common.Address, evm *EVM, gas uint64) ([]byte, uint64, error) {
gas, err := debitRequiredGas(c, input, gas)
if err != nil {
return nil, gas, err
}

if caller != params.AuthorizedRequestVerificationAddress {
return nil, fmt.Errorf("Unable to call tranfer from unpermissioned address")
return nil, gas, fmt.Errorf("Unable to call requestVerification from unpermissioned address")
}
_, err := types.DecodeVerificationRequest(input)
_, err = types.DecodeVerificationRequest(input)
if err != nil {
log.Error("[Celo] Unable to decode verification request", "err", err)
return nil, err
return nil, gas, err
} else {
return input, nil
return input, gas, nil
}
}

Expand All @@ -403,15 +453,20 @@ func (c *getCoinbase) RequiredGas(input []byte) uint64 {
return params.GetCoinbaseGas
}

func (c *getCoinbase) Run(input []byte, caller common.Address, evm *EVM) ([]byte, error) {
func (c *getCoinbase) Run(input []byte, caller common.Address, evm *EVM, gas uint64) ([]byte, uint64, error) {
gas, err := debitRequiredGas(c, input, gas)
if err != nil {
return nil, gas, err
}

var blockNumber, parsingSuccess = math.ParseBig256(hexutil.Encode(input[0:32]))

if !parsingSuccess {
return input, fmt.Errorf("Error parsing block number:" + hexutil.Encode(input[0:32]))
return input, gas, fmt.Errorf("Error parsing block number:" + hexutil.Encode(input[0:32]))
}

var coinbase = evm.Context.GetCoinbase(blockNumber.Uint64())
return coinbase.Bytes(), nil
return coinbase.Bytes(), gas, nil
}

// Native transfer contract to make Celo Gold ERC20 compatible.
Expand All @@ -421,22 +476,23 @@ func (c *transfer) RequiredGas(input []byte) uint64 {
return params.TxGas
}

// Ensures that the input is parsable as a VerificationRequest.
func (c *transfer) Run(input []byte, caller common.Address, evm *EVM) ([]byte, error) {
func (c *transfer) Run(input []byte, caller common.Address, evm *EVM, gas uint64) ([]byte, uint64, error) {
if caller != params.AuthorizedTransferAddress {
return nil, fmt.Errorf("Unable to call tranfer from unpermissioned address")
return nil, gas, fmt.Errorf("Unable to call transfer from unpermissioned address")
}
from := common.BytesToAddress(input[0:32])
to := common.BytesToAddress(input[32:64])
var parsed bool
value, parsed := math.ParseBig256(hexutil.Encode(input[64:96]))
if !parsed {
return nil, fmt.Errorf("Error parsing transfer: unable to parse value from " + hexutil.Encode(input[64:96]))
return nil, gas, fmt.Errorf("Error parsing transfer: unable to parse value from " + hexutil.Encode(input[64:96]))
}
// Fail if we're trying to transfer more than the available balance
if !evm.Context.CanTransfer(evm.StateDB, from, value) {
return nil, ErrInsufficientBalance
return nil, gas, ErrInsufficientBalance
}
evm.Transfer(evm.StateDB, from, to, value)
return input, nil

gas, err := evm.TobinTransfer(evm.StateDB, from, to, gas, value)

return input, gas, err
}
Loading

0 comments on commit 45396b5

Please sign in to comment.