Skip to content
This repository has been archived by the owner on Aug 13, 2019. It is now read-only.

EIP 196, 197, 198 #24

Merged
merged 41 commits into from
May 30, 2019
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
6e5dc6a
implement bn256 precompiles
noot Apr 21, 2019
4d1aa1e
Merge branch 'development' of github.com:eth-classic/go-ethereum into…
noot May 8, 2019
91467d7
implement bigModExp
noot May 13, 2019
0da961e
add comments and test
noot May 13, 2019
85d37d6
Merge branch 'development' of github.com:eth-classic/go-ethereum into…
noot May 21, 2019
e22f273
Merge branch 'development' into elizabeth/precompiles
noot May 21, 2019
04c1464
Merge branch 'development' into elizabeth/precompiles
noot May 22, 2019
8480d31
run precompile tests
noot May 22, 2019
4822719
skip failing tests for now
noot May 22, 2019
82ae34a
add distinction between atlantis and pre-atlantis precompiles
noot May 22, 2019
ab8c308
refactor precompiles to separate pre and post atlantis
noot May 23, 2019
e2f366d
Merge branch 'development' into elizabeth/precompiles
noot May 23, 2019
4524150
implement bn256 precompiles
noot Apr 21, 2019
0659d38
implement bigModExp
noot May 13, 2019
0d88004
add comments and test
noot May 13, 2019
4493f9b
run precompile tests
noot May 22, 2019
660c614
skip failing tests for now
noot May 22, 2019
aeda162
add distinction between atlantis and pre-atlantis precompiles
noot May 22, 2019
672f1f4
refactor precompiles to separate pre and post atlantis
noot May 23, 2019
ad3f211
Merge branch 'elizabeth/precompiles' of github.com:eth-classic/go-eth…
noot May 23, 2019
7545a93
fix conflicts
noot May 23, 2019
5118de0
merge with development
noot May 28, 2019
15c031c
fix ecrecover edge case
noot May 28, 2019
b29c1a2
implement bn256 precompiles
noot Apr 21, 2019
ef98ce4
implement bigModExp
noot May 13, 2019
f8f0541
add comments and test
noot May 13, 2019
4edbe8f
run precompile tests
noot May 22, 2019
c61bb9d
skip failing tests for now
noot May 22, 2019
28a3b9f
add distinction between atlantis and pre-atlantis precompiles
noot May 22, 2019
2827568
refactor precompiles to separate pre and post atlantis
noot May 23, 2019
e47f273
implement bn256 precompiles
noot Apr 21, 2019
888b3cb
implement bigModExp
noot May 13, 2019
fb89e4c
add comments and test
noot May 13, 2019
37f402b
run precompile tests
noot May 22, 2019
e4a4329
fix ecrecover edge case
noot May 28, 2019
55eb882
Merge branch 'elizabeth/precompiles' of github.com:eth-classic/go-eth…
noot May 28, 2019
998acfe
update go.mod
noot May 28, 2019
3ea1bc4
remove skip for previously failing tests
noot May 28, 2019
57d05e6
attempt to fix go.mod
noot May 28, 2019
27955de
attempt to fix go.mod
noot May 28, 2019
6863e1c
fix go.mod termiu version
noot May 28, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ coverage.txt
.vscode

accounts/testdata/keystore/accounts.db
*sublime-project
*sublime-workspace

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ew

16 changes: 16 additions & 0 deletions common/integer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package common

const (
MaxInt8 = 1<<7 - 1
MinInt8 = -1 << 7
MaxInt16 = 1<<15 - 1
MinInt16 = -1 << 15
MaxInt32 = 1<<31 - 1
MinInt32 = -1 << 31
MaxInt64 = 1<<63 - 1
MinInt64 = -1 << 63
MaxUint8 = 1<<8 - 1
MaxUint16 = 1<<16 - 1
MaxUint32 = 1<<32 - 1
MaxUint64 = 1<<64 - 1
)
2 changes: 1 addition & 1 deletion core/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
} else {
if !env.Db().Exist(*address) {
//no account may change state from non-existent to existent-but-empty. Refund sender.
if vm.Precompiled[(*address).Str()] == nil && env.RuleSet().IsAtlantis(env.BlockNumber()) && value.BitLen() == 0 {
if vm.PrecompiledAtlantis[(*address).Str()] == nil && env.RuleSet().IsAtlantis(env.BlockNumber()) && value.BitLen() == 0 {
caller.ReturnGas(gas, gasPrice)
return nil, common.Address{}, nil
}
Expand Down
272 changes: 256 additions & 16 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,39 @@ import (
"github.com/eth-classic/go-ethereum/logger/glog"
)

var (
big0 = big.NewInt(0)
big1 = big.NewInt(1)
big4 = big.NewInt(4)
big8 = big.NewInt(8)
big16 = big.NewInt(16)
big32 = big.NewInt(32)
big64 = big.NewInt(64)
big96 = big.NewInt(96)
big480 = big.NewInt(480)
big1024 = big.NewInt(1024)
big3072 = big.NewInt(3072)
big199680 = big.NewInt(199680)
)

const (
EcrecoverGas uint64 = 3000 // Elliptic curve sender recovery gas price
Sha256BaseGas uint64 = 60 // Base price for a SHA256 operation
Sha256PerWordGas uint64 = 12 // Per-word price for a SHA256 operation
Ripemd160BaseGas uint64 = 600 // Base price for a RIPEMD160 operation
Ripemd160PerWordGas uint64 = 120 // Per-word price for a RIPEMD160 operation
IdentityBaseGas uint64 = 15 // Base price for a data copy operation
IdentityPerWordGas uint64 = 3 // Per-work price for a data copy operation
ModExpQuadCoeffDiv uint64 = 20 // Divisor for the quadratic particle of the big int modular exponentiation
Bn256AddGas uint64 = 500 // Gas needed for an elliptic curve addition
Bn256ScalarMulGas uint64 = 40000 // Gas needed for an elliptic curve scalar multiplication
Bn256PairingBaseGas uint64 = 100000 // Base price for an elliptic curve pairing check
Bn256PairingPerPointGas uint64 = 80000 // Per-point price for an elliptic curve pairing check
)

// PrecompiledAccount represents a native ethereum contract
type PrecompiledAccount struct {
Gas func(l int) *big.Int
Gas func(in []byte) *big.Int
fn func(in []byte) []byte
}

Expand All @@ -38,46 +68,156 @@ func (self PrecompiledAccount) Call(in []byte) []byte {
}

// Precompiled contains the default set of ethereum contracts
var Precompiled = PrecompiledContracts()
var PrecompiledPreAtlantis = PrecompiledContractsPreAtlantis()
var PrecompiledAtlantis = PrecompiledContractsAtlantis()

// PrecompiledContractsPreAtlantis returns the default set of precompiled ethereum
// contracts defined by the ethereum yellow paper pre-Atlantis.
func PrecompiledContractsPreAtlantis() map[string]*PrecompiledAccount {
return map[string]*PrecompiledAccount{
// ECRECOVER
string(common.LeftPadBytes([]byte{1}, 20)): {func(in []byte) *big.Int {
noot marked this conversation as resolved.
Show resolved Hide resolved
return big.NewInt(3000)
}, ecrecoverFunc},

// SHA256
string(common.LeftPadBytes([]byte{2}, 20)): {func(in []byte) *big.Int {
l := len(in)
n := big.NewInt(int64(l+31) / 32)
n.Mul(n, big.NewInt(12))
return n.Add(n, big.NewInt(60))
}, sha256Func},

// RIPEMD160
string(common.LeftPadBytes([]byte{3}, 20)): {func(in []byte) *big.Int {
l := len(in)
n := big.NewInt(int64(l+31) / 32)
n.Mul(n, big.NewInt(120))
return n.Add(n, big.NewInt(600))
}, ripemd160Func},

// PrecompiledContracts returns the default set of precompiled ethereum
// contracts defined by the ethereum yellow paper.
func PrecompiledContracts() map[string]*PrecompiledAccount {
// memCpy
string(common.LeftPadBytes([]byte{4}, 20)): {func(in []byte) *big.Int {
l := len(in)
n := big.NewInt(int64(l+31) / 32)
n.Mul(n, big.NewInt(3))
return n.Add(n, big.NewInt(15))
}, memCpy},
}
}

// PrecompiledContractsAtlantis returns the default set of precompiled ethereum
// contracts defined by the ethereum yellow paper plus bigModExp and bn256 add,
// scalar mul, and pairing
func PrecompiledContractsAtlantis() map[string]*PrecompiledAccount {
return map[string]*PrecompiledAccount{
// ECRECOVER
string(common.LeftPadBytes([]byte{1}, 20)): {func(l int) *big.Int {
string(common.LeftPadBytes([]byte{1}, 20)): {func(in []byte) *big.Int {
return big.NewInt(3000)
}, ecrecoverFunc},

// SHA256
string(common.LeftPadBytes([]byte{2}, 20)): {func(l int) *big.Int {
string(common.LeftPadBytes([]byte{2}, 20)): {func(in []byte) *big.Int {
l := len(in)
n := big.NewInt(int64(l+31) / 32)
n.Mul(n, big.NewInt(12))
return n.Add(n, big.NewInt(60))
}, sha256Func},

// RIPEMD160
string(common.LeftPadBytes([]byte{3}, 20)): {func(l int) *big.Int {
string(common.LeftPadBytes([]byte{3}, 20)): {func(in []byte) *big.Int {
l := len(in)
n := big.NewInt(int64(l+31) / 32)
n.Mul(n, big.NewInt(120))
return n.Add(n, big.NewInt(600))
}, ripemd160Func},

string(common.LeftPadBytes([]byte{4}, 20)): {func(l int) *big.Int {
// memCpy
string(common.LeftPadBytes([]byte{4}, 20)): {func(in []byte) *big.Int {
l := len(in)
n := big.NewInt(int64(l+31) / 32)
n.Mul(n, big.NewInt(3))
return n.Add(n, big.NewInt(15))
}, memCpy},

// string(common.LeftPadBytes([]byte{5}, 20)): {func(l int) *big.Int {
// n := big.NewInt(int64(l+31) / 32)
// n.Mul(n, big.NewInt(3))
// return n.Add(n, big.NewInt(20))
// }, bigModExp},
// bigModExp
string(common.LeftPadBytes([]byte{5}, 20)): {func(in []byte) *big.Int {
var (
baseLen = new(big.Int).SetBytes(getData(in, big.NewInt(0), big32))
expLen = new(big.Int).SetBytes(getData(in, big32, big32))
modLen = new(big.Int).SetBytes(getData(in, big64, big32))
)
if len(in) > 96 {
in = in[96:]
} else {
in = in[:0]
}
// Retrieve the head 32 bytes of exp for the adjusted exponent length
var expHead *big.Int
if big.NewInt(int64(len(in))).Cmp(baseLen) <= 0 {
expHead = new(big.Int)
} else {
if expLen.Cmp(big32) > 0 {
expHead = new(big.Int).SetBytes(getData(in, baseLen, big32))
} else {
expHead = new(big.Int).SetBytes(getData(in, baseLen, expLen))
}
}
// Calculate the adjusted exponent length
var msb int
if bitlen := expHead.BitLen(); bitlen > 0 {
msb = bitlen - 1
}
adjExpLen := new(big.Int)
if expLen.Cmp(big32) > 0 {
adjExpLen.Sub(expLen, big32)
adjExpLen.Mul(big8, adjExpLen)
}
adjExpLen.Add(adjExpLen, big.NewInt(int64(msb)))

// Calculate the gas cost of the operation
gas := new(big.Int).Set(common.BigMax(modLen, baseLen))
switch {
case gas.Cmp(big64) <= 0:
gas.Mul(gas, gas)
case gas.Cmp(big1024) <= 0:
gas = new(big.Int).Add(
new(big.Int).Div(new(big.Int).Mul(gas, gas), big4),
new(big.Int).Sub(new(big.Int).Mul(big96, gas), big3072),
)
default:
gas = new(big.Int).Add(
new(big.Int).Div(new(big.Int).Mul(gas, gas), big16),
new(big.Int).Sub(new(big.Int).Mul(big480, gas), big199680),
)
}
gas.Mul(gas, common.BigMax(adjExpLen, big1))
gas.Div(gas, new(big.Int).SetUint64(ModExpQuadCoeffDiv))

if gas.BitLen() > 64 {
return big.NewInt(1 << 63 - 1)
}
return gas
}, bigModExp},

string(common.LeftPadBytes([]byte{6}, 20)): {func(l int) *big.Int {
// bn256Add
string(common.LeftPadBytes([]byte{6}, 20)): {func(in []byte) *big.Int {
return big.NewInt(500)
}, bn256Add},

// bn256ScalarMul
string(common.LeftPadBytes([]byte{7}, 20)): {func(in []byte) *big.Int {
return big.NewInt(40000)
}, bn256ScalarMul},

// bn256Pairing
string(common.LeftPadBytes([]byte{8}, 20)): {func(in []byte) *big.Int {
l := len(in)
n := big.NewInt(100000)
p := big.NewInt(int64(l/192))
p.Mul(p, big.NewInt(80000))
return n.Add(n, p)
}, bn256Pairing},
}
}

Expand Down Expand Up @@ -125,6 +265,47 @@ func memCpy(in []byte) []byte {
return in
}

func bigModExp(in []byte) []byte {
var (
baseLen = new(big.Int).SetBytes(getData(in, big0, big32))
expLen = new(big.Int).SetBytes(getData(in, big32, big32))
modLen = new(big.Int).SetBytes(getData(in, big64, big32))
)

if len(in) > 96 {
in = in[96:]
} else {
in = in[:0]
}

// Handle a special case when both the base and mod length is zero
if baseLen.Cmp(big0) == 0 && modLen.Cmp(big0) == 0 {
return []byte{}
}
// Retrieve the operands and execute the exponentiation
var (
base = new(big.Int).SetBytes(getData(in, big0, baseLen))
exp = new(big.Int).SetBytes(getData(in, baseLen, expLen))
mod = new(big.Int).SetBytes(getData(in, big.NewInt(0).Add(baseLen, expLen), modLen))
)
if mod.BitLen() == 0 {
// Modulo 0 is undefined, return zero
return common.LeftPadBytes([]byte{}, int(modLen.Int64()))
}
return common.LeftPadBytes(base.Exp(base, exp, mod).Bytes(), int(modLen.Int64()))
}

var (
// true32Byte is returned if the bn256 pairing check succeeds.
true32Byte = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}

// false32Byte is returned if the bn256 pairing check fails.
false32Byte = make([]byte, 32)

// errBadPairingInput is returned if the bn256 pairing input is invalid.
//errBadPairingInput = errors.New("bad elliptic curve pairing size")
)

// newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point,
// returning it, or an error if the point is invalid.
func newCurvePoint(blob []byte) (*bn256.G1, error) {
Expand All @@ -135,6 +316,65 @@ func newCurvePoint(blob []byte) (*bn256.G1, error) {
return p, nil
}

// newTwistPoint unmarshals a binary blob into a bn256 elliptic curve point,
// returning it, or an error if the point is invalid.
func newTwistPoint(blob []byte) (*bn256.G2, error) {
p := new(bn256.G2)
if _, err := p.Unmarshal(blob); err != nil {
return nil, err
}
return p, nil
}

func bn256Add(in []byte) []byte {
return in
x, err := newCurvePoint(getData(in, big.NewInt(0), big.NewInt(64)))
if err != nil {
return nil
}
y, err := newCurvePoint(getData(in, big.NewInt(64), big.NewInt(64)))
if err != nil {
return nil
}
res := new(bn256.G1)
res.Add(x, y)
return res.Marshal()
}

func bn256ScalarMul(in []byte) []byte {
p, err := newCurvePoint(getData(in, big.NewInt(0), big.NewInt(64)))
if err != nil {
return nil
}
res := new(bn256.G1)
res.ScalarMult(p, new(big.Int).SetBytes(getData(in, big.NewInt(64), big.NewInt(32))))
return res.Marshal()
}

func bn256Pairing(in []byte) []byte {
// Handle some corner cases cheaply
if len(in)%192 > 0 {
return nil
}
// Convert the input into a set of coordinates
var (
cs []*bn256.G1
ts []*bn256.G2
)
for i := 0; i < len(in); i += 192 {
c, err := newCurvePoint(in[i : i+64])
if err != nil {
return nil
}
t, err := newTwistPoint(in[i+64 : i+192])
if err != nil {
return nil
}
cs = append(cs, c)
ts = append(ts, t)
}
// Execute the pairing checks and return the results
if bn256.PairingCheck(cs, ts) {
return true32Byte
}
return false32Byte
}
13 changes: 10 additions & 3 deletions core/vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,16 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
defer evm.env.SetDepth(evm.env.Depth() - 1)

if contract.CodeAddr != nil {
if p := Precompiled[contract.CodeAddr.Str()]; p != nil {
return evm.RunPrecompiled(p, input, contract)
if evm.env.RuleSet().IsAtlantis(evm.env.BlockNumber()) {
if p := PrecompiledAtlantis[contract.CodeAddr.Str()]; p != nil {
return evm.RunPrecompiled(p, input, contract)
}
} else {
if p := PrecompiledPreAtlantis[contract.CodeAddr.Str()]; p != nil {
return evm.RunPrecompiled(p, input, contract)
}
}

}

// Don't bother with the execution if there's no code.
Expand Down Expand Up @@ -386,7 +393,7 @@ func calculateGasAndSize(gasTable *GasTable, env Environment, contract *Contract

// RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go
func (evm *EVM) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (ret []byte, err error) {
gas := p.Gas(len(input))
gas := p.Gas(input)
if contract.UseGas(gas) {
ret = p.Call(input)

Expand Down
Loading