From 879e7d266d6330c007e2d1e8c7f6f4926b875fb6 Mon Sep 17 00:00:00 2001 From: 1m1 Date: Sat, 21 Oct 2023 21:39:49 -0700 Subject: [PATCH 01/39] Abstract --- EIPS/eip-EVM+.md | 104 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 EIPS/eip-EVM+.md diff --git a/EIPS/eip-EVM+.md b/EIPS/eip-EVM+.md new file mode 100644 index 00000000000000..daafe538f78d7a --- /dev/null +++ b/EIPS/eip-EVM+.md @@ -0,0 +1,104 @@ +--- +title: EVM+ +description: Decimal math in the EVM +author: 1m1 (@1m1-github) +discussions-to: https://ethereum-magicians.org/t/decimal-math-on-evm/16194 +status: Draft +type: Standards Track +category: Core +created: 2023-10-22 +--- + + +## Abstract + +This EIP adds *decimal fixed* OPCODEs for arithmetic (DECADD, DECNEG, DECMUL, DECINV) and expression of all elementary functions (DECEXP, DECLN, DECSIN). All decimal values upto the maximal precision allowed by a int256 coefficient and exponent are represented exactly, as c*10^q. All implemented algorithms converge for all inputs given enough precision, as chosen by the user. All calculations are deterministic and gas is embedded bottom-up. + +## Motivation + + + +## Specification + + + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +## Rationale + + + +TBD + +## Backwards Compatibility + + + +No backward compatibility issues found. + +## Test Cases + + + +## Reference Implementation + + + +## Security Considerations + + + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From a01a6a53c43607f1f210fce5890ae05e1d2caf68 Mon Sep 17 00:00:00 2001 From: 1m1 Date: Sat, 21 Oct 2023 22:32:49 -0700 Subject: [PATCH 02/39] progress --- EIPS/eip-EVM+.md | 95 ++++--- assets/eip-EVM+/decimal_fixed_test.go | 346 ++++++++++++++++++++++++++ 2 files changed, 403 insertions(+), 38 deletions(-) create mode 100644 assets/eip-EVM+/decimal_fixed_test.go diff --git a/EIPS/eip-EVM+.md b/EIPS/eip-EVM+.md index daafe538f78d7a..063ebfa99e680d 100644 --- a/EIPS/eip-EVM+.md +++ b/EIPS/eip-EVM+.md @@ -12,10 +12,23 @@ created: 2023-10-22 ## Abstract -This EIP adds *decimal fixed* OPCODEs for arithmetic (DECADD, DECNEG, DECMUL, DECINV) and expression of all elementary functions (DECEXP, DECLN, DECSIN). All decimal values upto the maximal precision allowed by a int256 coefficient and exponent are represented exactly, as c*10^q. All implemented algorithms converge for all inputs given enough precision, as chosen by the user. All calculations are deterministic and gas is embedded bottom-up. +This EIP adds *decimal fixed* OPCODEs for arithmetic (DECADD, DECNEG, DECMUL, DECINV) and expression of all elementary functions (DECEXP, DECLN, DECSIN). All decimal values upto the maximal precision allowed by a int256 coefficient and exponent are represented exactly, as c*10^q. All implemented algorithms converge for all inputs given enough precision, as chosen by the user. All calculations are deterministic and gas is embedded bottom-up. Allowing high precision decimal elementary functions invites the worlds of mathematical finance, machine learning, science, digital art, games and others to Ethereum. ## Motivation +Currently, to take a power, a^b, of non integer values, requires vast amounts of Solidity code. +The simplest task in trading e.g. is to convert volatilities from yearly to daily, which involves taking the 16th root. + +Giving users/devs the same ability that scientific calculators have allows for the creation of apps with higher complexity. + +### Why decimal? +A simple value like 0.1 cannot be represented finitely in binary. Decimal types are much closer to the vast majority of numerical calculations run by humans. + +### eVm + +The EVM is a virtual machine and thereby not restricted by hardware. Usually, assembly languages provide OPCODES that are mimic the ability of hardware. In a virtual machine, we have no such limitations and nothing stops us from adding more complex OPCODEs, as long as fair gas is provided. At the same time, we do not want to clutter the OPCODEs library. EXP, LN and SIN are universal functions that open the path to: powers, trigonometry, integrals, differential equations, machine learning, digital art, etc. + + +all the above OPCODEs are deterministic, hence the gas cost can be determined. at the same time, the calculations are complex and depend on the input. -TBD +it is crucial to have accurate gas costs to avoid energy attacks on nodes. -## Backwards Compatibility +to this end, i have wrapped the underlying uint256 lib with gas accumulation (https://github.com/1m1-github/go-ethereum-plus/blob/main/core/vm/uint256_wrapped.go). this gives a bottom-up approach to calculating gas, by running the OPCODE. - +## Backwards Compatibility No backward compatibility issues found. ## Test Cases - +../assets/eip-EVM+/decimal_fixed_test.go ## Reference Implementation - ## Security Considerations - - -Needs discussion. +There are no security considerations, as long as numerical correctness is guaranteed and gas is collected fairly. ## Copyright diff --git a/assets/eip-EVM+/decimal_fixed_test.go b/assets/eip-EVM+/decimal_fixed_test.go new file mode 100644 index 00000000000000..510ba1ada1b084 --- /dev/null +++ b/assets/eip-EVM+/decimal_fixed_test.go @@ -0,0 +1,346 @@ +// 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 . + +package vm + +import ( + "fmt" + "testing" + + "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" +) + +var PRECISION = uint256.NewInt(10) + +func (d *Decimal) String() string { + c := new(uint256.Int).Set(&d.c) + q := new(uint256.Int).Set(&d.q) + cs := "" + if c.Sign() == -1 { + cs = "-" + c.Neg(c) + } + qs := "" + if q.Sign() == -1 { + qs = "-" + q.Neg(q) + } + return fmt.Sprintf("%v%v*10^%v%v", cs, c.Dec(), qs, q.Dec()) +} + +func BenchmarkOpAdd(b *testing.B) { + intArgs := []*uint256.Int{uint256.NewInt(987349875), uint256.NewInt(987349875), uint256.NewInt(987349875), uint256.NewInt(987349875)} + benchmarkOpDec(b, intArgs, opAdd) +} + +func BenchmarkOpDecAdd(b *testing.B) { + intArgs := []*uint256.Int{PRECISION, uint256.NewInt(987349875), uint256.NewInt(987349875), uint256.NewInt(987349875), uint256.NewInt(987349875)} + benchmarkOpDec(b, intArgs, opDecAdd) +} + +func BenchmarkOpDecNeg(b *testing.B) { + intArgs := []*uint256.Int{uint256.NewInt(987349875), uint256.NewInt(987349875)} + benchmarkOpDec(b, intArgs, opDecNeg) +} + +func BenchmarkOpDecMul(b *testing.B) { + intArgs := []*uint256.Int{PRECISION, uint256.NewInt(987349875), uint256.NewInt(987349875), uint256.NewInt(987349875), uint256.NewInt(987349875)} + benchmarkOpDec(b, intArgs, opDecMul) +} + +func BenchmarkOpDecInv(b *testing.B) { + // opDecInv benchmark does not depend on precision + intArgs := []*uint256.Int{PRECISION, MINUS_ONE_INT256, uint256.NewInt(1)} + benchmarkOpDec(b, intArgs, opDecInv) +} + +func BenchmarkOpDecExp(b *testing.B) { + // opDecExp benchmark depends on steps + steps := uint256.NewInt(10) + intArgs := []*uint256.Int{steps, PRECISION, uint256.NewInt(0), uint256.NewInt(1)} + fmt.Println("BenchmarkOpDecExp steps=", steps) + benchmarkOpDec(b, intArgs, opDecExp) +} + +func BenchmarkOpDecLn(b *testing.B) { + // opDecExp benchmark depends on steps + steps := uint256.NewInt(10) + intArgs := []*uint256.Int{steps, PRECISION, uint256.NewInt(0), uint256.NewInt(2)} + fmt.Println("BenchmarkOpDecLn steps=", steps) + benchmarkOpDec(b, intArgs, opDecLn) +} + +func BenchmarkOpDecSin(b *testing.B) { + // opDecExp benchmark depends on precision + steps := uint256.NewInt(10) + intArgs := []*uint256.Int{steps, PRECISION, uint256.NewInt(0), uint256.NewInt(1)} + fmt.Println("BenchmarkOpDecSin steps=", steps) + benchmarkOpDec(b, intArgs, opDecSin) +} + +func benchmarkOpDec(b *testing.B, intArgs []*uint256.Int, op executionFunc) { + var ( + env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + scope = &ScopeContext{nil, stack, nil} + evmInterpreter = NewEVMInterpreter(env) + ) + + env.interpreter = evmInterpreter + + pc := uint64(0) + b.ResetTimer() + for i := 0; i < b.N; i++ { + for _, arg := range intArgs { + stack.push(arg) + } + op(&pc, evmInterpreter, scope) + stack.pop() + stack.pop() + } + b.StopTimer() +} + +func TestSignedCmp(t *testing.T) { + var gas uint64 + + // b := uint256.NewInt(15) + // a := uint256.NewInt(23) + a := new(uint256.Int).Neg(uint256.NewInt(14)) + b := new(uint256.Int).Neg(uint256.NewInt(15)) + c := SignedCmp(a, b, &gas) + fmt.Println(c) +} + +func TestDecAdd(t *testing.T) { + var gas uint64 + + tests := []struct { + a Decimal + b Decimal + c Decimal + }{ + {*createDecimal(uint256.NewInt(5), ZERO_INT256, &gas), *createDecimal(uint256.NewInt(121), MINUS_ONE_INT256, &gas), *createDecimal(uint256.NewInt(171), MINUS_ONE_INT256, &gas)}, + {*createDecimal(uint256.NewInt(5), ZERO_INT256, &gas), *createDecimal(uint256.NewInt(121), ZERO_INT256, &gas), *createDecimal(uint256.NewInt(126), ZERO_INT256, &gas)}, + {*createDecimal(new(uint256.Int).Neg(TWO_INT256), MINUS_ONE_INT256, &gas), *createDecimal(uint256.NewInt(8), MINUS_ONE_INT256, &gas), *createDecimal(uint256.NewInt(6), MINUS_ONE_INT256, &gas)}, + {*createDecimal(uint256.NewInt(5), MINUS_ONE_INT256, &gas), *createDecimal(new(uint256.Int).Neg(TWO_INT256), ZERO_INT256, &gas), *createDecimal(new(uint256.Int).Neg(uint256.NewInt(15)), MINUS_ONE_INT256, &gas)}, + } + for _, tt := range tests { + var out Decimal + gas = 0 + out.Add(&tt.a, &tt.b, PRECISION, &gas) + fmt.Println(gas) + // fmt.Println("a", showDecimal(&tt.a), "b", showDecimal(&tt.b), "out", showDecimal(&out), "c", showDecimal(&tt.c)) + + if !out.eq(&tt.c, PRECISION, &gas) { + t.Fatal(tt.a, tt.b, out, tt.c) + } + } +} + +func TestDecNeg(t *testing.T) { + var gas uint64 + + tests := []struct { + a Decimal + b Decimal + }{ + {*createDecimal(uint256.NewInt(2), ZERO_INT256, &gas), *createDecimal(new(uint256.Int).Neg(TWO_INT256), ZERO_INT256, &gas)}, + {*createDecimal(uint256.NewInt(5), MINUS_ONE_INT256, &gas), *createDecimal(new(uint256.Int).Neg(FIVE_INT256), MINUS_ONE_INT256, &gas)}, + } + for _, tt := range tests { + var out Decimal + gas = 0 + out.Neg(&tt.a, &gas) + fmt.Println(gas) + // fmt.Println("a", showDecimal(&tt.a)) + // fmt.Println("b", showDecimal(&tt.b)) + // fmt.Println("out", showDecimal(&out)) + + if !out.eq(&tt.b, PRECISION, &gas) { + t.Fatal(tt.a, tt.b, out) + } + } +} + +func TestDecMul(t *testing.T) { + var gas uint64 + + tests := []struct { + a Decimal + b Decimal + c Decimal + }{ + {*createDecimal(uint256.NewInt(2), ZERO_INT256, &gas), *createDecimal(uint256.NewInt(2), ZERO_INT256, &gas), *createDecimal(uint256.NewInt(4), ZERO_INT256, &gas)}, + {*createDecimal(uint256.NewInt(2), ZERO_INT256, &gas), *createDecimal(uint256.NewInt(5), MINUS_ONE_INT256, &gas), *createDecimal(uint256.NewInt(1), ZERO_INT256, &gas)}, + {*createDecimal(new(uint256.Int).Neg(TWO_INT256), ZERO_INT256, &gas), *createDecimal(uint256.NewInt(5), MINUS_ONE_INT256, &gas), *createDecimal(MINUS_ONE_INT256, ZERO_INT256, &gas)}, + {*createDecimal(new(uint256.Int).Neg(TWO_INT256), ZERO_INT256, &gas), *createDecimal(new(uint256.Int).Neg(FIVE_INT256), MINUS_ONE_INT256, &gas), *createDecimal(uint256.NewInt(1), ZERO_INT256, &gas)}, + } + for _, tt := range tests { + var out Decimal + gas = 0 + out.Mul(&tt.a, &tt.b, PRECISION, &gas) + fmt.Println(gas) + if !out.eq(&tt.c, PRECISION, &gas) { + t.Fatal(tt.a, tt.b, out, tt.c) + } + } +} + +func TestDecInv(t *testing.T) { + var gas uint64 + + tests := []struct { + a Decimal + b Decimal + }{ + {*copyDecimal(ONE_DECIMAL, &gas), *copyDecimal(ONE_DECIMAL, &gas)}, + {*createDecimal(uint256.NewInt(2), ZERO_INT256, &gas), *createDecimal(uint256.NewInt(5), MINUS_ONE_INT256, &gas)}, + {*createDecimal(new(uint256.Int).Neg(uint256.NewInt(20)), MINUS_ONE_INT256, &gas), *createDecimal(new(uint256.Int).Neg(FIVE_INT256), MINUS_ONE_INT256, &gas)}, + {*createDecimal(uint256.NewInt(2), ONE_INT256, &gas), *createDecimal(uint256.NewInt(5), new(uint256.Int).Neg(TWO_INT256), &gas)}, + {*createDecimal(uint256.NewInt(2), MINUS_ONE_INT256, &gas), *createDecimal(uint256.NewInt(5), ZERO_INT256, &gas)}, + } + for _, tt := range tests { + var out Decimal + gas = 0 + out.Inv(&tt.a, PRECISION, &gas) + fmt.Println(gas) + // fmt.Println("a", showDecimal(&tt.a), "out", showDecimal(&out), "b", showDecimal(&tt.b)) + + if !out.eq(&tt.b, PRECISION, &gas) { + t.Fatal(tt.a, out, tt.b) + } + } +} + +func TestDecNormalize(t *testing.T) { + var gas uint64 + + LARGE_TEN := uint256.NewInt(10) + LARGE_TEN.Exp(LARGE_TEN, uint256.NewInt(75)) + + TEN_TEN := uint256.NewInt(10) + TEN_TEN.Exp(TEN_TEN, uint256.NewInt(10)) + + NEG_45 := new(uint256.Int).Neg(uint256.NewInt(45)) + NEG_55 := new(uint256.Int).Neg(uint256.NewInt(55)) + // NEG_77 := new(uint256.Int).Neg(uint256.NewInt(77)) + NEG_75 := new(uint256.Int).Neg(uint256.NewInt(75)) + // NEG_76 := new(uint256.Int).Neg(uint256.NewInt(76)) + + var TEN_48, FIVE_48, MINUS_FIVE_48 uint256.Int + TEN_48.Exp(uint256.NewInt(10), uint256.NewInt(48)) + FIVE_48.Mul(uint256.NewInt(5), &TEN_48) + MINUS_FIVE_48.Neg(&FIVE_48) + MINUS_49 := new(uint256.Int).Neg(uint256.NewInt(49)) + MINUS_5 := new(uint256.Int).Neg(FIVE_INT256) + + tests := []struct { + a Decimal + b Decimal + rounded bool + }{ + {*createDecimal(uint256.NewInt(15), MINUS_ONE_INT256, &gas), *createDecimal(uint256.NewInt(15), MINUS_ONE_INT256, &gas), false}, + {*copyDecimal(ONE_DECIMAL, &gas), *copyDecimal(ONE_DECIMAL, &gas), false}, + {*createDecimal(uint256.NewInt(100), new(uint256.Int).Neg(TWO_INT256), &gas), *copyDecimal(ONE_DECIMAL, &gas), false}, + {*createDecimal(LARGE_TEN, NEG_75, &gas), *copyDecimal(ONE_DECIMAL, &gas), false}, + {*createDecimal(TEN_TEN, NEG_55, &gas), *createDecimal(ONE_INT256, NEG_45, &gas), true}, + {*createDecimal(&MINUS_FIVE_48, MINUS_49, &gas), *createDecimal(MINUS_5, MINUS_ONE_INT256, &gas), false}, + } + for _, tt := range tests { + var out Decimal + gas = 0 + out.normalize(&tt.a, PRECISION, tt.rounded, &gas) + fmt.Println(gas) + // fmt.Println("normalize", tt.a.String(), out.String(), tt.b.String()) + + if !out.eq(&tt.b, PRECISION, &gas) { + t.Fatal(tt.a, out, tt.b) + } + } +} + +func TestDecExp(t *testing.T) { + var gas uint64 + + tests := []struct { + a Decimal + steps uint256.Int + b Decimal + }{ + {*copyDecimal(ONE_DECIMAL, &gas), *uint256.NewInt(10), *createDecimal(uint256.NewInt(27182815251), new(uint256.Int).Neg(TEN_INT256), &gas)}, + {*createDecimal(MINUS_ONE_INT256, uint256.NewInt(0), &gas), *uint256.NewInt(10), *createDecimal(uint256.NewInt(3678791887), new(uint256.Int).Neg(TEN_INT256), &gas)}, + } + for _, tt := range tests { + + var out Decimal + gas = 0 + out.Exp(&tt.a, PRECISION, &tt.steps, &gas) + fmt.Println(gas) + // fmt.Println(out.String()) + + if !out.eq(&tt.b, PRECISION, &gas) { + t.Fatal(tt.a, out, tt.b) + } + } +} + +func TestDecLn(t *testing.T) { + var gas uint64 + + tests := []struct { + a Decimal + steps uint256.Int + b Decimal + }{ + {*ONE_DECIMAL, *uint256.NewInt(10), *createDecimal(uint256.NewInt(5849609375), new(uint256.Int).Neg(TEN_INT256), &gas)}, + {*createDecimal(uint256.NewInt(5), MINUS_ONE_INT256, &gas), *uint256.NewInt(10), *createDecimal(uint256.NewInt(5849609375), new(uint256.Int).Neg(TEN_INT256), &gas)}, + {*createDecimal(uint256.NewInt(11), ZERO_INT256, &gas), *uint256.NewInt(5), *createDecimal(uint256.NewInt(5849609375), new(uint256.Int).Neg(TEN_INT256), &gas)}, + } + for _, tt := range tests { + var out Decimal + gas = 0 + out.Ln(&tt.a, PRECISION, &tt.steps, &gas) + fmt.Println(gas) + // fmt.Println(out.String()) + // if !out.eq(&tt.b, PRECISION) { + // t.Fatal(tt.a, out, tt.b) + // } + } +} + +func TestDecSin(t *testing.T) { + var gas uint64 + + tests := []struct { + a Decimal + steps uint256.Int + b Decimal + }{ + {*copyDecimal(ONE_DECIMAL, &gas), *uint256.NewInt(10), *createDecimal(uint256.NewInt(8414709849), new(uint256.Int).Neg(TEN_INT256), &gas)}, + } + for _, tt := range tests { + var out Decimal + gas = 0 + out.Sin(&tt.a, PRECISION, &tt.steps, &gas) + fmt.Println(gas) + // fmt.Println(out.String()) + if !out.eq(&tt.b, PRECISION, &gas) { + t.Fatal(tt.a, out, tt.b) + } + } +} From a5cb785456a16147f05a7cb10c1501ac98b8b1b8 Mon Sep 17 00:00:00 2001 From: 1m1 Date: Sat, 21 Oct 2023 22:44:47 -0700 Subject: [PATCH 03/39] definitions --- EIPS/eip-EVM+.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/EIPS/eip-EVM+.md b/EIPS/eip-EVM+.md index 063ebfa99e680d..8fd893d279f433 100644 --- a/EIPS/eip-EVM+.md +++ b/EIPS/eip-EVM+.md @@ -41,6 +41,33 @@ The EVM is a virtual machine and thereby not restricted by hardware. Usually, as ## Specification +### Decimal + +A decimal is defined as + +c * 10^q + +where c and q are int256. + +Notationwise: +a = ac * 10^aq +b = bc * 10^bq +etc. + +### OPCODE defs + +0xd0 DECADD a+b -> c : (ac, aq, bc, bq, precision) -> (cc, cq) +0xd1 DECNEG -a -> b : (ac, aq) -> (bc, bq) +0xd2 DECMUL a*b -> c : (ac, aq, bc, bq, precision) -> (cc, cq) +0xd3 DECINV 1/a -> b : (ac, aq, precision) -> (bc, bq) +0xd4 DECEXP exp(a) -> b : (ac, aq, precision, steps) -> (bc, bq) +0xd5 DECLN ln(a) -> b : (ac, aq, precision, steps) -> (bc, bq) +0xd6 DECSIN sin(a) -> b : (ac, aq, precision, steps) -> (bc, bq) + +precision is the # of digits kept during all calculations. steps for DECEXP and DECSIN are the # of Taylor expansion steps. steps for DECLN is the depth of the continued fractions expansion. + +### Why these functions? + The proposed functions (+,-,*,/,exp,ln,sin) form a small set that combined enable all calculation of all elementary functions, which includes the sets of sums, products, roots and compositions of finitely many polynomial, rational, trigonometric, hyperbolic, and exponential functions, including their inverse functions. a^b = exp(b * ln(a)) gives us powers and polynomials. @@ -56,7 +83,13 @@ For the same reason, we have DECINV instead of DECDIV. DECSUB(a,b) = DECADD(a,DECNEG(b)) DECDIV(a,b) = DECMUL(a,DECINV(b)) -### +### DECEXP, DECSIN via Taylor series + +The Taylor series of exp and sin converge everywhere and fast. The error falls as fast as the factorial of steps. + +### DECLN via continued fractions + +Ln converges fast using continued fractions within the interval ]0,2]. The implementation scales the input into this interval and scales the result back correctly. ### math/big From b6bae33c2097160c5f690c30e5dec775224ad942 Mon Sep 17 00:00:00 2001 From: 1m1 Date: Sat, 21 Oct 2023 22:47:53 -0700 Subject: [PATCH 04/39] functional --- EIPS/eip-EVM+.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-EVM+.md b/EIPS/eip-EVM+.md index 8fd893d279f433..83f1d7cf76ffb1 100644 --- a/EIPS/eip-EVM+.md +++ b/EIPS/eip-EVM+.md @@ -12,7 +12,7 @@ created: 2023-10-22 ## Abstract -This EIP adds *decimal fixed* OPCODEs for arithmetic (DECADD, DECNEG, DECMUL, DECINV) and expression of all elementary functions (DECEXP, DECLN, DECSIN). All decimal values upto the maximal precision allowed by a int256 coefficient and exponent are represented exactly, as c*10^q. All implemented algorithms converge for all inputs given enough precision, as chosen by the user. All calculations are deterministic and gas is embedded bottom-up. Allowing high precision decimal elementary functions invites the worlds of mathematical finance, machine learning, science, digital art, games and others to Ethereum. +This EIP adds *decimal fixed* OPCODEs for arithmetic (DECADD, DECNEG, DECMUL, DECINV) and expression of all elementary functions (DECEXP, DECLN, DECSIN). All decimal values upto the maximal precision allowed by a int256 coefficient and exponent are represented exactly, as c*10^q. All implemented algorithms converge for all inputs given enough precision, as chosen by the user. All calculations are deterministic and gas is embedded bottom-up. Allowing high precision decimal elementary functions invites the worlds of mathematical finance, machine learning, science, digital art, games and others to Ethereum. The implementation is functional. ## Motivation From 7bdf0af2749940f04f77729c2746529118b310b2 Mon Sep 17 00:00:00 2001 From: 1m1 Date: Sat, 21 Oct 2023 22:49:44 -0700 Subject: [PATCH 05/39] main file --- EIPS/eip-EVM+.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-EVM+.md b/EIPS/eip-EVM+.md index 83f1d7cf76ffb1..ddbf1f943862fe 100644 --- a/EIPS/eip-EVM+.md +++ b/EIPS/eip-EVM+.md @@ -143,10 +143,10 @@ No backward compatibility issues found. The following is a view of the complete and functional implementation: https://github.com/ethereum/go-ethereum/compare/master...1m1-github:go-ethereum-plus:main +The main file is: core/vm/decimal_fixed.go I see that external links are not allowed, I hope github is internal, as I else do not know how to show it inline. - ## Security Considerations There are no security considerations, as long as numerical correctness is guaranteed and gas is collected fairly. From e8ae5e20b3da06e8f88a144bfc65984ee515c49f Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Sun, 22 Oct 2023 05:25:36 -0700 Subject: [PATCH 06/39] reference implementation i know that the reference implementation is not abstract enough. it is running code, which should be clear. rather showcase the capability and if interest, write the tech specs for other clients. --- EIPS/eip-EVM+.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-EVM+.md b/EIPS/eip-EVM+.md index ddbf1f943862fe..2659de5220029e 100644 --- a/EIPS/eip-EVM+.md +++ b/EIPS/eip-EVM+.md @@ -141,11 +141,11 @@ No backward compatibility issues found. ## Reference Implementation -The following is a view of the complete and functional implementation: +The following is a view of the complete and functional implementation in golang: https://github.com/ethereum/go-ethereum/compare/master...1m1-github:go-ethereum-plus:main The main file is: core/vm/decimal_fixed.go -I see that external links are not allowed, I hope github is internal, as I else do not know how to show it inline. +If the community likes this EIP by trying it out using the above implementation, I could then formalize the "Reference Implementation" abstractly for other clients to follow. ## Security Considerations From a712b74e6e1eb09da5ba1cb1daea1d12237c3ba6 Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Sun, 22 Oct 2023 18:17:28 -0700 Subject: [PATCH 07/39] main code. could be used as reference. --- assets/eip-EVM+/decimal_fixed.go.txt | 536 +++++++++++++++++++++++++++ 1 file changed, 536 insertions(+) create mode 100644 assets/eip-EVM+/decimal_fixed.go.txt diff --git a/assets/eip-EVM+/decimal_fixed.go.txt b/assets/eip-EVM+/decimal_fixed.go.txt new file mode 100644 index 00000000000000..4568b334e7508f --- /dev/null +++ b/assets/eip-EVM+/decimal_fixed.go.txt @@ -0,0 +1,536 @@ +// math based on https://github.com/JuliaMath/Decimals.jl + +package vm + +// Decimal struct and constructors + +// c * 10^q +type Decimal struct { + c int256 // coefficient, interpreted as int256 + q int256 // exponent, interpreted as int256 +} + +func copyDecimal(d *Decimal, gas *uint64) *Decimal { + return createDecimal(&d.c, &d.q, gas) +} +func createDecimal(_c, _q *int256, gas *uint64) *Decimal { + var c, q int256 + Set(_c, &c, gas) + Set(_q, &q, gas) + return &Decimal{c, q} +} + +// CONSTANTS +var GLOBAL_GAS uint64 + +var MINUS_ONE_INT256 = new(int256).Neg(ONE_INT256) +var ZERO_INT256 = New(0, &GLOBAL_GAS) +var ONE_INT256 = New(1, &GLOBAL_GAS) +var TWO_INT256 = New(2, &GLOBAL_GAS) +var FIVE_INT256 = New(5, &GLOBAL_GAS) +var TEN_INT256 = New(10, &GLOBAL_GAS) + +var MINUS_ONE_DECIMAL = createDecimal(MINUS_ONE_INT256, ZERO_INT256, &GLOBAL_GAS) +var HALF_DECIMAL = createDecimal(FIVE_INT256, MINUS_ONE_INT256, &GLOBAL_GAS) +var ZERO_DECIMAL = createDecimal(ZERO_INT256, ONE_INT256, &GLOBAL_GAS) +var ONE_DECIMAL = createDecimal(ONE_INT256, ZERO_INT256, &GLOBAL_GAS) +var TWO_DECIMAL = createDecimal(TWO_INT256, ZERO_INT256, &GLOBAL_GAS) +var TEN_DECIMAL = createDecimal(TEN_INT256, ZERO_INT256, &GLOBAL_GAS) + +// OPCODE functions + +// a + b +func (out *Decimal) Add(a, b *Decimal, precision *int256, gas *uint64) *Decimal { + // ok even if out == a || out == b + + ca := add_helper(a, b, gas) + cb := add_helper(b, a, gas) + + Add(&ca, &cb, &out.c, gas) + Set(min(&a.q, &b.q, gas), &out.q, gas) + + out.normalize(out, precision, false, gas) + + return out +} + +// -a +func (out *Decimal) Neg(a *Decimal, gas *uint64) *Decimal { + // ok even if out == a + Neg(&a.c, &out.c, gas) + Set(&a.q, &out.q, gas) + // no need to normalize + return out +} + +// a * b +func (out *Decimal) Mul(a, b *Decimal, precision *int256, gas *uint64) *Decimal { + // ok even if out == a || out == b + Mul(&a.c, &b.c, &out.c, gas) + Add(&a.q, &b.q, &out.q, gas) + out.normalize(out, precision, false, gas) + return out +} + +// 1 / a +func (out *Decimal) Inv(a *Decimal, precision *int256, gas *uint64) *Decimal { + // ok even if out == a + + var precision_m_aq int256 + Sub(precision, &a.q, &precision_m_aq, gas) + if SignedCmp(&precision_m_aq, ZERO_INT256, gas) == -1 { + panic("precision_m_aq NEGATIVE") + } + + Exp(TEN_INT256, &precision_m_aq, &precision_m_aq, gas) // save space: precision_m_aq not needed after + signedDiv(&precision_m_aq, &a.c, &out.c, gas) + Neg(precision, &out.q, gas) + + out.normalize(out, precision, false, gas) + + return out +} + +// e^a +func (out *Decimal) Exp(_a *Decimal, precision, steps *int256, gas *uint64) *Decimal { + a := copyDecimal(_a, gas) // in case out == _a + + // out = 1 + Set(ONE_INT256, &out.c, gas) + Set(ZERO_INT256, &out.q, gas) + + if a.isZero(gas) { + return out + } + + var factorial_inv Decimal + a_power := copyDecimal(ONE_DECIMAL, gas) + factorial := copyDecimal(ONE_DECIMAL, gas) + factorial_next := copyDecimal(ZERO_DECIMAL, gas) + + for i := New(1, gas); Cmp(steps, i, gas) == -1; Add(i, ONE_INT256, i, gas) { // step 0 skipped as out set to 1 + a_power.Mul(a_power, a, precision, gas) // a^i + factorial_next.Add(factorial_next, ONE_DECIMAL, precision, gas) // i++ + factorial.Mul(factorial, factorial_next, precision, gas) // i! + factorial_inv.Inv(factorial, precision, gas) // 1/i! + factorial_inv.Mul(&factorial_inv, a_power, precision, gas) // store a^i/i! in factorial_inv as not needed anymore + out.Add(out, &factorial_inv, precision, gas) // out += a^i/i! + } + + return out +} + +// 0 < _a +func (out *Decimal) Ln(_a *Decimal, precision, steps *int256, gas *uint64) *Decimal { + a := copyDecimal(_a, gas) + + if a.isNegative(gas) { + panic("Ln: need 0 < x") + } + + // ln(1) = 0 + if a.isOne(gas) { + Set(ZERO_INT256, &out.c, gas) + Set(ONE_INT256, &out.q, gas) + return out + } + + // adjust x + // divide x by 10 until x in [0,2] + adjust := New(0, gas) + for { + if a.lessThan(TWO_DECIMAL, precision, gas) { + break + } + + // x /= 10 + Add(&a.q, MINUS_ONE_INT256, &a.q, gas) + Add(adjust, ONE_INT256, adjust, gas) + } + + // ln works with 1+x + a.Add(a, MINUS_ONE_DECIMAL, precision, gas) + + // main + out.ln(a, precision, steps, gas) + + // readjust back: ln(a*10^n) = ln(a)+n*ln(10) + var LN10 Decimal + LN10.ln10(precision, steps, gas) + adjustDec := createDecimal(adjust, ZERO_INT256, gas) + LN10.Mul(adjustDec, &LN10, precision, gas) + out.Add(out, &LN10, precision, gas) + + return out +} + +// sin(a) +func (out *Decimal) Sin(_a *Decimal, precision, steps *int256, gas *uint64) *Decimal { + a := copyDecimal(_a, gas) // in case out == _a + + // out = a + Set(&a.c, &out.c, gas) + Set(&a.q, &out.q, gas) + + if a.isZero(gas) || Cmp(ONE_INT256, precision, gas) == 0 { + return out + } + + var a_squared, factorial_inv Decimal + a_squared.Mul(a, a, precision, gas) + a_power := copyDecimal(ONE_DECIMAL, gas) + factorial := copyDecimal(ONE_DECIMAL, gas) + factorial_next := copyDecimal(ONE_DECIMAL, gas) + negate := true + + for i := New(1, gas); Cmp(steps, i, gas) == -1; Add(i, ONE_INT256, i, gas) { // step 0 skipped as out set to a + a_power.Mul(a_power, &a_squared, precision, gas) // a^(2i+1) + + factorial_next.Add(factorial_next, ONE_DECIMAL, precision, gas) // i++ + factorial.Mul(factorial, factorial_next, precision, gas) // i!*2i + factorial_next.Add(factorial_next, ONE_DECIMAL, precision, gas) // i++ + factorial.Mul(factorial, factorial_next, precision, gas) // (2i+1)! + + factorial_inv.Inv(factorial, precision, gas) // 1/(2i+1)! + factorial_inv.Mul(&factorial_inv, a_power, precision, gas) // store a^(2i+1)/(2i+1)! in factorial_inv as not needed anymore + if negate { + factorial_inv.Neg(&factorial_inv, gas) // (-1)^i*a^(2i+1)/(2i+1)! + } + negate = !negate + + out.Add(out, &factorial_inv, precision, gas) // out += (-1)^i*a^(2i+1)/(2i+1)! + } + + return out +} + +// convenience methods + +func DecAdd(ac, aq, bc, bq, precision *int256, gas *uint64) (cc, cq *int256) { + a := createDecimal(ac, aq, gas) + b := createDecimal(bc, bq, gas) + a.Add(a, b, precision, gas) + cc = &a.c + cq = &a.q + return +} +func DecNeg(ac, aq *int256, gas *uint64) (bc, bq *int256) { + a := createDecimal(ac, aq, gas) + a.Neg(a, gas) + bc = &a.c + bq = &a.q + return +} +func DecMul(ac, aq, bc, bq, precision *int256, gas *uint64) (cc, cq *int256) { + a := createDecimal(ac, aq, gas) + b := createDecimal(bc, bq, gas) + a.Mul(a, b, precision, gas) + cc = &a.c + cq = &a.q + return +} +func DecInv(ac, aq, precision *int256, gas *uint64) (bc, bq *int256) { + a := createDecimal(ac, aq, gas) + a.Inv(a, precision, gas) + bc = &a.c + bq = &a.q + return +} +func DecExp(ac, aq, precision, steps *int256, gas *uint64) (bc, bq *int256) { + a := createDecimal(ac, aq, gas) + a.Exp(a, precision, steps, gas) + bc = &a.c + bq = &a.q + return +} +func DecLn(ac, aq, precision, steps *int256, gas *uint64) (bc, bq *int256) { + a := createDecimal(ac, aq, gas) + a.Ln(a, precision, steps, gas) + bc = &a.c + bq = &a.q + return +} +func DecSin(ac, aq, precision, steps *int256, gas *uint64) (bc, bq *int256) { + a := createDecimal(ac, aq, gas) + a.Sin(a, precision, steps, gas) + bc = &a.c + bq = &a.q + return +} + +// helpers + +// -1 if a < b +// +// 0 if a == b +// 1 if b < a +func SignedCmp(a, b *int256, gas *uint64) int { + c := a.Cmp(b) + + if c == 0 { // a == b + return 0 + } + + as := a.Sign() + bs := b.Sign() + + if as == 0 { + return -bs + } + if bs == 0 { + return as + } + + if c == -1 { // a < b + if a.Sign() == b.Sign() { + return -1 // a < b + } else { + return 1 // b < a + } + } + + // c == 1 <=> b < a + if a.Sign() == b.Sign() { + return 1 // b < a + } else { + return -1 // a < b + } +} + +// min(a, b) +func min(a, b *int256, gas *uint64) (c *int256) { + if SignedCmp(a, b, gas) == -1 { + return a + } else { + return b + } +} + +// a == 0 +func (a *Decimal) isZero(gas *uint64) bool { + return IsZero(&a.c, gas) +} + +// a should be normalized +// a == 1 ? +func (a *Decimal) isOne(gas *uint64) bool { + return Cmp(ONE_INT256, &a.c, gas) == 0 && IsZero(&a.q, gas) // Cmp ok vs SignedCmp when comparing to zero +} + +// a < 0 ? +func (a *Decimal) isNegative(gas *uint64) bool { + return Sign(&a.c, gas) == -1 +} + +func (d2 *Decimal) eq(d1 *Decimal, precision *int256, gas *uint64) bool { + d1_zero := d1.isZero(gas) + d2_zero := d2.isZero(gas) + if d1_zero || d2_zero { + return d1_zero == d2_zero + } + + d1.normalize(d1, precision, false, gas) + d2.normalize(d2, precision, false, gas) + return Cmp(&d2.c, &d1.c, gas) == 0 && Cmp(&d2.q, &d1.q, gas) == 0 // Cmp ok vs SignedCmp when comparing to zero +} + +// a < b +func (a *Decimal) lessThan(b *Decimal, precision *int256, gas *uint64) bool { + var diff Decimal + diff.Add(a, diff.Neg(b, gas), precision, gas) + return Sign(&diff.c, gas) == -1 +} + +func signedDiv(numerator, denominator, out *int256, gas *uint64) *int256 { + sn := Sign(numerator, gas) + sd := Sign(denominator, gas) + if sn == 0 && sd == 0 { + out = nil + return nil + } + if sn == 0 { + out = New(0, gas) + return out + } + + n := *numerator + if sn == -1 { + Neg(numerator, &n, gas) + } + + d := *denominator + if sd == -1 { + Neg(denominator, &d, gas) + } + + Div(&n, &d, out, gas) + + if (sn == -1) != (sd == -1) { + Neg(out, out, gas) + } + + return out +} + +// c = (-1)^d1.s * d1.c * 10^max(d1.q - d2.q, 0) +func add_helper(d1, d2 *Decimal, gas *uint64) (c int256) { + var exponent_diff int256 + Sub(&d1.q, &d2.q, &exponent_diff, gas) + if Sign(&exponent_diff, gas) == -1 { + exponent_diff = *ZERO_INT256 // shallow copy ok + } + + Exp(TEN_INT256, &exponent_diff, &c, gas) + Mul(&d1.c, &c, &c, gas) + + return c +} + +// remove trailing zeros from coefficient +func find_num_trailing_zeros_signed_DECIMAL256(a *int256, gas *uint64) (p, ten_power *int256) { + var b int256 + Set(a, &b, gas) + if Sign(&b, gas) == -1 { + Neg(&b, &b, gas) + } + + p = New(0, gas) + ten_power = New(10, gas) + if Cmp(ZERO_INT256, &b, gas) != 0 { // if b != 0 // Cmp ok vs SignedCmp when comparing to zero + for { + var m int256 + Mod(&b, ten_power, &m, gas) + if Cmp(ZERO_INT256, &m, gas) != 0 { // if b % 10^(p+1) != 0 // Cmp ok vs SignedCmp when comparing to zero + break + } + Add(p, ONE_INT256, p, gas) + Mul(ten_power, TEN_INT256, ten_power, gas) // 10^(p+1) + } + } + Div(ten_power, TEN_INT256, ten_power, gas) // all positive + + return p, ten_power +} + +// remove trailing zeros in coefficient +func (out *Decimal) normalize(a *Decimal, precision *int256, rounded bool, gas *uint64) *Decimal { + // ok even if out == a + + p, ten_power := find_num_trailing_zeros_signed_DECIMAL256(&a.c, gas) + signedDiv(&a.c, ten_power, &out.c, gas) // does not change polarity [in case out == a] + + a_neg := a.isNegative(gas) + if Cmp(ZERO_INT256, &out.c, gas) != 0 || a_neg { // Cmp ok vs SignedCmp when comparing to zero + Add(&a.q, p, &out.q, gas) + } else { + Set(ZERO_INT256, &out.q, gas) + } + + if rounded { + return out + } + + out.round(out, precision, true, gas) + return out +} + +func (out *Decimal) round(a *Decimal, precision *int256, normal bool, gas *uint64) *Decimal { + // ok if out == a + + var shift, ten_power int256 + Add(precision, &a.q, &shift, gas) + + if SignedCmp(&shift, ZERO_INT256, gas) == 1 || SignedCmp(&shift, &a.q, gas) == -1 { + if normal { + Set(&a.c, &out.c, gas) + Set(&a.q, &out.q, gas) + return out + } + out.normalize(a, precision, true, gas) + return out + } + + Neg(&shift, &shift, gas) + Exp(TEN_INT256, &shift, &ten_power, gas) + signedDiv(&a.c, &ten_power, &out.c, gas) + Add(&a.q, &shift, &out.q, gas) + if normal { + return out + } + out.normalize(out, precision, true, gas) + return out +} + +// ln helpers + +// https://en.wikipedia.org/wiki/Natural_logarithm#Continued_fractions +// using CF (continued fractions) for ln(1+x/y). we set y=1 +// ln(1+a), a in [-1,1] +func (out *Decimal) ln(a *Decimal, precision, steps *int256, gas *uint64) *Decimal { + var two_y_plus_x Decimal + two_y_plus_x.Add(a, TWO_DECIMAL, precision, gas) + + step := New(1, gas) + + // recursion of continued fraction + out2 := ln_recur(a, &two_y_plus_x, precision, steps, step, gas) + Set(&out2.c, &out.c, gas) + Set(&out2.q, &out.q, gas) + out.Inv(out, precision, gas) + + // 2x / out + var two_x Decimal + two_x.Mul(a, TWO_DECIMAL, precision, gas) + out.Mul(out, &two_x, precision, gas) + + return out +} + +// ln10 needed for scaling +func (out *Decimal) ln10(precision, steps *int256, gas *uint64) *Decimal { + THREE_INT256 := New(3, gas) + THREE_DECIMAL256 := createDecimal(THREE_INT256, ZERO_INT256, gas) + ONE_OVER_FOUR := createDecimal(New(25, gas), new(int256).Neg(TWO_INT256), gas) + THREE_OVER_125 := createDecimal(New(24, gas), new(int256).Neg(THREE_INT256), gas) + var a, b Decimal + a.ln(ONE_OVER_FOUR, precision, steps, gas) + b.ln(THREE_OVER_125, precision, steps, gas) + a.Mul(&a, TEN_DECIMAL, precision, gas) + b.Mul(&b, THREE_DECIMAL256, precision, gas) + out.Add(&a, &b, precision, gas) + return out +} + +// out !== a +func ln_recur(a, two_y_plus_x *Decimal, precision, max_steps, step *int256, gas *uint64) *Decimal { + var out Decimal + + // (2*step-1)*(2+x) + stepDec := createDecimal(step, ZERO_INT256, gas) + stepDec.Mul(stepDec, TWO_DECIMAL, precision, gas) + stepDec.Add(stepDec, MINUS_ONE_DECIMAL, precision, gas) + out.Mul(stepDec, two_y_plus_x, precision, gas) + + // end recursion? + if Cmp(max_steps, step, gas) == 0 { + return &out + } + + // recursion + Add(step, ONE_INT256, step, gas) + r := ln_recur(a, two_y_plus_x, precision, max_steps, step, gas) + Sub(step, ONE_INT256, step, gas) + r.Inv(r, precision, gas) + + // (step*x)^2 + stepDec2 := createDecimal(step, ZERO_INT256, gas) + stepDec2.Mul(stepDec2, a, precision, gas) + stepDec2.Mul(stepDec2, stepDec2, precision, gas) + + r.Mul(stepDec2, r, precision, gas) + r.Neg(r, gas) + + out.Add(&out, r, precision, gas) + + return &out +} From 314afe18ffd2c9fc3b4d902ef39ec5bb4e445aad Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Sun, 22 Oct 2023 18:23:31 -0700 Subject: [PATCH 08/39] Rename decimal_fixed.go.txt to decimal_fixed.go --- assets/eip-EVM+/{decimal_fixed.go.txt => decimal_fixed.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename assets/eip-EVM+/{decimal_fixed.go.txt => decimal_fixed.go} (100%) diff --git a/assets/eip-EVM+/decimal_fixed.go.txt b/assets/eip-EVM+/decimal_fixed.go similarity index 100% rename from assets/eip-EVM+/decimal_fixed.go.txt rename to assets/eip-EVM+/decimal_fixed.go From d1449a0c18dc8d7577eeecdb6ad0646edc067b39 Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Sun, 22 Oct 2023 18:31:20 -0700 Subject: [PATCH 09/39] bottom-up gas (sorry, to the hackathon ppl, I don't why I forgot to add the main files to the assets. no sleep. adding now. had a link to the same. --- assets/eip-EVM+/uint256_wrapped.go.txt | 81 ++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 assets/eip-EVM+/uint256_wrapped.go.txt diff --git a/assets/eip-EVM+/uint256_wrapped.go.txt b/assets/eip-EVM+/uint256_wrapped.go.txt new file mode 100644 index 00000000000000..e9a02a692e9be1 --- /dev/null +++ b/assets/eip-EVM+/uint256_wrapped.go.txt @@ -0,0 +1,81 @@ +package vm + +import ( + "github.com/holiman/uint256" + "github.com/ethereum/go-ethereum/params" +) + +type int256 = uint256.Int + +func New(a uint64, gas *uint64) *int256 { + return uint256.NewInt(a) +} + +func Add(a, b, out *int256, gas *uint64) *int256 { + out.Add(a, b) + *gas += GasFastestStep + return out +} + +func Set(a, out *int256, gas *uint64) *int256 { + out.Set(a) + return out +} + +func Sub(a, b, out *int256, gas *uint64) *int256 { + out.Sub(a, b) + *gas += GasFastestStep + return out +} + +func Mod(a, b, out *int256, gas *uint64) *int256 { + out.Mod(a, b) + *gas += GasFastStep + return out +} + +func Sign(a *int256, gas *uint64) int { + return a.Sign() +} + +func Cmp(a, b *int256, gas *uint64) int { + *gas += GasFastStep + return b.Cmp(a) +} + +func Exp(a, b, out *int256, gas *uint64) *int256 { + out.Exp(a, b) + + expByteLen := uint64((b.BitLen() + 7) / 8) + *gas += expByteLen * params.ExpByteEIP158 + + return out +} + +func Mul(a, b, out *int256, gas *uint64) *int256 { + out.Mul(a, b) + *gas += GasFastStep + return out +} + +func Div(a, b, out *int256, gas *uint64) *int256 { + out.Div(a, b) + *gas += GasFastStep + return out +} + +func Neg(a, out *int256, gas *uint64) *int256 { + out.Neg(a) // also has new, maybe more gas? + *gas += GasFastestStep + return out +} + +func IsZero(a *int256, gas *uint64) bool { + return a.IsZero() +} + +func Lsh(a *int256, n uint, out *int256, gas *uint64) *int256 { + out.Lsh(a, n) + *gas += GasFastestStep // ? + return out +} \ No newline at end of file From 79cf7f3169065ea3fd5c639993a74c369d36b15f Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Sun, 22 Oct 2023 18:31:58 -0700 Subject: [PATCH 10/39] Rename uint256_wrapped.go.txt to uint256_wrapped.go Android... --- assets/eip-EVM+/{uint256_wrapped.go.txt => uint256_wrapped.go} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename assets/eip-EVM+/{uint256_wrapped.go.txt => uint256_wrapped.go} (99%) diff --git a/assets/eip-EVM+/uint256_wrapped.go.txt b/assets/eip-EVM+/uint256_wrapped.go similarity index 99% rename from assets/eip-EVM+/uint256_wrapped.go.txt rename to assets/eip-EVM+/uint256_wrapped.go index e9a02a692e9be1..adba5df5fc4c1b 100644 --- a/assets/eip-EVM+/uint256_wrapped.go.txt +++ b/assets/eip-EVM+/uint256_wrapped.go @@ -78,4 +78,4 @@ func Lsh(a *int256, n uint, out *int256, gas *uint64) *int256 { out.Lsh(a, n) *gas += GasFastestStep // ? return out -} \ No newline at end of file +} From d05c846f25df8a62a2d41b6177befeca96718cdb Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Sun, 22 Oct 2023 19:26:13 -0700 Subject: [PATCH 11/39] gasEVMPlusEmulate.go --- assets/eip-EVM+/gasEVMPlusEmulate.go | 55 ++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 assets/eip-EVM+/gasEVMPlusEmulate.go diff --git a/assets/eip-EVM+/gasEVMPlusEmulate.go b/assets/eip-EVM+/gasEVMPlusEmulate.go new file mode 100644 index 00000000000000..7b1ae533ba3c62 --- /dev/null +++ b/assets/eip-EVM+/gasEVMPlusEmulate.go @@ -0,0 +1,55 @@ +// THE BELOW CODE IS ADDED TO core/vm/gas_table.go + +// emulate op for EVMPlus gas +// double estimate gas for actual run +// gas emulation cost +type GasOpFunc func(inputs []int256, gas *uint64) +func gasEVMPlusEmulate(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64, nStack int, op GasOpFunc) (uint64, error) { + + inputs := make([]int256, nStack) + for i := 0; i < nStack; i++ { + inputs[i] = stack.pop() + } + gas := uint64(0) + op(inputs, &gas) + for i := nStack - 1; 0 <= i ; i-- { + stack.push(&inputs[i]) + } + + return 2*gas, nil // double to account for emulation plus actual compute +} +func gasEVMPlusDECADD(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + nStack := 5 + op := func(inputs []int256, gas *uint64) {DecAdd(&inputs[0], &inputs[1], &inputs[2], &inputs[3], &inputs[4], gas)} + return gasEVMPlusEmulate(evm, contract, stack, mem, memorySize, nStack, op) +} +func gasEVMPlusDECNEG(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + nStack := 2 + op := func(inputs []int256, gas *uint64) {DecNeg(&inputs[0], &inputs[1], gas)} + return gasEVMPlusEmulate(evm, contract, stack, mem, memorySize, nStack, op) +} +func gasEVMPlusDECMUL(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + nStack := 5 + op := func(inputs []int256, gas *uint64) {DecMul(&inputs[0], &inputs[1], &inputs[2], &inputs[3], &inputs[4], gas)} + return gasEVMPlusEmulate(evm, contract, stack, mem, memorySize, nStack, op) +} +func gasEVMPlusDECINV(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + nStack := 3 + op := func(inputs []int256, gas *uint64) {DecInv(&inputs[0], &inputs[1], &inputs[2], gas)} + return gasEVMPlusEmulate(evm, contract, stack, mem, memorySize, nStack, op) +} +func gasEVMPlusDECEXP(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + nStack := 4 + op := func(inputs []int256, gas *uint64) {DecExp(&inputs[0], &inputs[1], &inputs[2], &inputs[3], gas)} + return gasEVMPlusEmulate(evm, contract, stack, mem, memorySize, nStack, op) +} +func gasEVMPlusDECLN(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + nStack := 4 + op := func(inputs []int256, gas *uint64) {DecLn(&inputs[0], &inputs[1], &inputs[2], &inputs[3], gas)} + return gasEVMPlusEmulate(evm, contract, stack, mem, memorySize, nStack, op) +} +func gasEVMPlusDECSIN(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + nStack := 4 + op := func(inputs []int256, gas *uint64) {DecSin(&inputs[0], &inputs[1], &inputs[2], &inputs[3], gas)} + return gasEVMPlusEmulate(evm, contract, stack, mem, memorySize, nStack, op) +} From c148e970e2ff708d68f7a1467ca60dd813859276 Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Sun, 22 Oct 2023 19:28:39 -0700 Subject: [PATCH 12/39] no links out --- EIPS/eip-EVM+.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/EIPS/eip-EVM+.md b/EIPS/eip-EVM+.md index 2659de5220029e..3c426e67238f32 100644 --- a/EIPS/eip-EVM+.md +++ b/EIPS/eip-EVM+.md @@ -121,11 +121,11 @@ all the above OPCODEs are deterministic, hence the gas cost can be determined. a it is crucial to have accurate gas costs to avoid energy attacks on nodes. -to this end, i have wrapped the underlying uint256 lib with gas accumulation (https://github.com/1m1-github/go-ethereum-plus/blob/main/core/vm/uint256_wrapped.go). this gives a bottom-up approach to calculating gas, by running the OPCODE. +to this end, i have wrapped the underlying uint256 lib with gas accumulation, as found in the reference implementation in ../assets/eip-EVM+/uint256_wrapped.go. this gives a bottom-up approach to calculating gas, by running the OPCODE. because the EVM interprator expects the gas cost before actually running the OPCODE, we are running the OPCODE twice. the first run, identical to the second, is to get the bottom-up gas cost, which is then doubled to account for the actual run plus the gas calculation. on top, we add a fixed emulation cost. -this gives an embedded gas calcuation, which works well for complex OPCODEs (see gasEVMPlusEmulate in https://github.com/1m1-github/go-ethereum-plus/blob/main/core/vm/gas_table.go). +this gives an embedded gas calcuation, which works well for complex OPCODEs (see gasEVMPlusEmulate in ../assets/eip-EVM+/gasEVMPlusEmulate.go). to remove the double gas, a future EIP would suggest the following: allow contract code to run whilst accumulating gas (at runtime) and panicking in case of limit breach, without requiring the cost in advance. this only works for contract code that is local, defined as code that only depends on the user input and the inner bytecode of the contract. local contracts cannot use state from the chain, nor make calls to other contracts. pure mathematical functions would e.g. be local contracts. local contracts are fully deterministic given the input, allowing a user to estimate gas costs offline (cheaper) and the EVM to panic at runtime, without knowing gas in advance. @@ -141,11 +141,7 @@ No backward compatibility issues found. ## Reference Implementation -The following is a view of the complete and functional implementation in golang: -https://github.com/ethereum/go-ethereum/compare/master...1m1-github:go-ethereum-plus:main -The main file is: core/vm/decimal_fixed.go - -If the community likes this EIP by trying it out using the above implementation, I could then formalize the "Reference Implementation" abstractly for other clients to follow. +The reference implementation is found in ../assets/eip-EVM+/decimal_fixed.go ## Security Considerations From 62ce641aa60baf853d1e4d495f84b03f8b0cdc56 Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Sun, 22 Oct 2023 19:32:55 -0700 Subject: [PATCH 13/39] EIP # 7543 assigned ? Co-authored-by: Andrew B Coathup <28278242+abcoathup@users.noreply.github.com> --- EIPS/eip-EVM+.md | 1 + 1 file changed, 1 insertion(+) diff --git a/EIPS/eip-EVM+.md b/EIPS/eip-EVM+.md index 3c426e67238f32..754337557227eb 100644 --- a/EIPS/eip-EVM+.md +++ b/EIPS/eip-EVM+.md @@ -1,4 +1,5 @@ --- +eip: 7543 title: EVM+ description: Decimal math in the EVM author: 1m1 (@1m1-github) From 1ad93ebacc3cd3244025a23a94486eea41e5231e Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Sun, 22 Oct 2023 19:35:49 -0700 Subject: [PATCH 14/39] better title and description --- EIPS/eip-EVM+.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-EVM+.md b/EIPS/eip-EVM+.md index 754337557227eb..de6dd1fc26d3e8 100644 --- a/EIPS/eip-EVM+.md +++ b/EIPS/eip-EVM+.md @@ -1,7 +1,7 @@ --- eip: 7543 -title: EVM+ -description: Decimal math in the EVM +title: EVM+: Decimal math in the EVM +description: OPCODEs to allow high precision decimal calculation of all elementary functions. author: 1m1 (@1m1-github) discussions-to: https://ethereum-magicians.org/t/decimal-math-on-evm/16194 status: Draft From 3b36ba59a4ebd9c029ed8671c04d3c5df6c984ef Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Sun, 22 Oct 2023 19:42:20 -0700 Subject: [PATCH 15/39] example smart contracts example Yul smart contracts to show simplicity provided by new OPCODEs --- assets/eip-EVM+/BlackScholes.yul | 270 ++++++++++++++++++++++ assets/eip-EVM+/Neuron.yul | 369 +++++++++++++++++++++++++++++++ 2 files changed, 639 insertions(+) create mode 100644 assets/eip-EVM+/BlackScholes.yul create mode 100644 assets/eip-EVM+/Neuron.yul diff --git a/assets/eip-EVM+/BlackScholes.yul b/assets/eip-EVM+/BlackScholes.yul new file mode 100644 index 00000000000000..ce4fa1d4d3fa4b --- /dev/null +++ b/assets/eip-EVM+/BlackScholes.yul @@ -0,0 +1,270 @@ +// solc --evm-version london --strict-assembly BlackScholes.yul >> BlackScholes.txt + +object "BlackScholes" { + code { + datacopy(0, dataoffset("runtime"), datasize("runtime")) + return(0, datasize("runtime")) + } + object "runtime" { + code { + // a = ac*10^aq is a decimal + // ac, aq int256 as 2's complement + + // Dispatcher + switch selector() + // callprice(int256,int256,int256,int256,int256,int256,int256,int256,int256,int256,int256,int256) + case 0x95ba71af { + // Sc, Sq, Kc, Kq, rc, rq, sc, sq, Tc, Tq, precision, steps + // 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352 + calldatacopy(0, 4, 384) + + let ac, aq := callprice() + sstore(0, ac) + sstore(1, aq) + } + default { + revert(0, 0) + } + function selector() -> s { + s := div(calldataload(0), 0x100000000000000000000000000000000000000000000000000000000) + } + + // BlackScholes + // S: underlying price + // K: strike + // r: interest + // s: volatility + // T: time + + function r_s2_T() { + let precision := mload(320) + + let sc := mload(192) + let sq := mload(224) + let s_sqr_c, s_sqr_q := dec_sqr(sc, sq, precision) + + let sigma_sqr_half_c, sigma_sqr_half_q := dec_div(s_sqr_c, s_sqr_q, 2, 0, precision) + + let rc := mload(128) + let rq := mload(160) + let r_p_s_c, r_p_s_q := dec_add(sigma_sqr_half_c, sigma_sqr_half_q, rc, rq, precision) + + let Tc := mload(256) + let Tq := mload(288) + let r_s2_T_c, r_s2_T_q := dec_mul(r_p_s_c, r_p_s_q, Tc, Tq, precision) + + mstore(384, r_s2_T_c) + mstore(416, r_s2_T_q) + } + + function ln_S_K() { + let precision := mload(320) + let steps := mload(352) + + let Sc := mload(0) + let Sq := mload(32) + let Kc := mload(64) + let Kq := mload(96) + let S_K_c, S_K_q := dec_div(Sc, Sq, Kc, Kq, precision) + let ln_S_K_c, ln_S_K_q := dec_ln(S_K_c, S_K_q, precision, steps) + + mstore(448, ln_S_K_c) + mstore(480, ln_S_K_q) + } + function d_plus_s_T() { + let precision := mload(320) + let steps := mload(352) + + let sc := mload(192) + let sq := mload(224) + let Tc := mload(256) + let Tq := mload(288) + let sqrt_T_c, sqrt_T_q := dec_sqrt(Tc, Tq, precision, steps) + let s_sqrt_T_c, s_sqrt_T_q := dec_mul(sc, sq, sqrt_T_c, sqrt_T_q, precision) + + mstore(384, s_sqrt_T_c) + mstore(416, s_sqrt_T_q) + } + function d_plus() { + let precision := mload(320) + let steps := mload(352) + + r_s2_T() + let r_s2_T_c := mload(384) + let r_s2_T_q := mload(416) + ln_S_K() + let ln_S_K_c := mload(448) + let ln_S_K_q := mload(480) + let ln_S_K_p_r_s2_T_c, ln_S_K_p_r_s2_T_q := dec_add(ln_S_K_c,ln_S_K_q, r_s2_T_c, r_s2_T_q, precision) + + d_plus_s_T() + let s_sqrt_T_c := mload(384) + let s_sqrt_T_q := mload(416) + + let d_plus_c, d_plus_q := dec_div(ln_S_K_p_r_s2_T_c, ln_S_K_p_r_s2_T_q, s_sqrt_T_c, s_sqrt_T_q, precision) + + mstore(448, d_plus_c) + mstore(480, d_plus_q) + } + function d_minus() { + let precision := mload(320) + + let d_plus_c := mload(448) + let d_plus_q := mload(480) + let s_sqrt_T_c := mload(384) + let s_sqrt_T_q := mload(416) + let d_minus_c, d_minus_q := dec_sub(d_plus_c, d_plus_q, s_sqrt_T_c, s_sqrt_T_q, precision) + + mstore(384, d_minus_c) + mstore(416, d_minus_q) + } + // approximation + // 1/(1+dec_exp(-1.65451*a)) + function CDF(ac, aq) -> bc, bq { + let precision := mload(320) + let steps := mload(352) + + let C := 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd79b5 // -165451 + let MINUS_FIVE := 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb // -5 + + let b1_c, b1_q := dec_mul(C, MINUS_FIVE, ac, aq, precision) + let b2_c, b2_q := dec_exp(b1_c, b1_q, precision, steps) + let b3_c, b3_q := dec_add(b2_c, b2_q, 1, 0, precision) + bc, bq := dec_inv(b3_c, b3_q, precision) + } + function cdf_dp_S() { + let precision := mload(320) + + let d_plus_c := mload(448) + let d_plus_q := mload(480) + let cdf_dp_c, cdf_dp_q := CDF(d_plus_c, d_plus_q) + + let Sc := mload(0) + let Sq := mload(32) + let cdf_dp_S_c, cdf_dp_S_q := dec_mul(Sc, Sq, cdf_dp_c, cdf_dp_q, precision) + + mstore(448, cdf_dp_S_c) + mstore(480, cdf_dp_S_q) + } + function cdf_dm_K_exp_r_T() { + let precision := mload(320) + let steps := mload(352) + + let rc := mload(128) + let rq := mload(160) + let r_n_c, r_n_q := dec_neg(rc, rq) + let Tc := mload(256) + let Tq := mload(288) + let r_T_c, r_T_q := dec_mul(r_n_c, r_n_q, Tc, Tq, precision) + let exp_r_T_c, exp_r_T_q := dec_exp(r_T_c, r_T_q, precision, steps) + let Kc := mload(64) + let Kq := mload(96) + let K_exp_r_T_c, K_exp_r_T_q := dec_mul(Kc, Kq, exp_r_T_c, exp_r_T_q, precision) + + mstore(384, K_exp_r_T_c) + mstore(416, K_exp_r_T_q) + } + function cdf_dm_K() { + let precision := mload(320) + let steps := mload(352) + + let d_minus_c := mload(384) + let d_minus_q := mload(416) + + cdf_dm_K_exp_r_T() + let K_exp_r_T_c := mload(384) + let K_exp_r_T_q := mload(416) + + let cdf_dm_c, cdf_dm_q := CDF(d_minus_c, d_minus_q) + let cdf_dm_K_c, cdf_dm_K_q := dec_mul(cdf_dm_c, cdf_dm_q, K_exp_r_T_c, K_exp_r_T_q, precision) + + mstore(384, cdf_dm_K_c) + mstore(416, cdf_dm_K_q) + } + function callprice() -> ac, aq { + let precision := mload(320) + + d_plus() + d_minus() + cdf_dp_S() + cdf_dm_K() + + let cdf_dm_K_c := mload(384) + let cdf_dm_K_q := mload(416) + let cdf_dp_S_c := mload(448) + let cdf_dp_S_q := mload(480) + + ac, aq := dec_sub(cdf_dp_S_c, cdf_dp_S_q, cdf_dm_K_c, cdf_dm_K_q, precision) + } + + // OPCODE -> function + + // a + b = c + function dec_add(ac, aq, bc, bq, precision) -> cc, cq { + cc, cq := verbatim_5i_2o(hex"d0", ac, aq, bc, bq, precision) + } + + // -a = b + function dec_neg(ac, aq) -> bc, bq { + bc, bq := verbatim_2i_2o(hex"d1", ac, aq) + } + + // a * b = c + function dec_mul(ac, aq, bc, bq, precision) -> cc, cq { + cc, cq := verbatim_5i_2o(hex"d2", ac, aq, bc, bq, precision) + } + + // 1 / a = b + function dec_inv(ac, aq, precision) -> bc, bq { + bc, bq := verbatim_3i_2o(hex"d3", ac, aq, precision) + } + + // dec_exp(a) = b + function dec_exp(ac, aq, precision, steps) -> bc, bq { + bc, bq := verbatim_4i_2o(hex"d4", ac, aq, precision, steps) + } + + // dec_ln(a) = b + function dec_ln(ac, aq, precision, steps) -> bc, bq { + bc, bq := verbatim_4i_2o(hex"d5", ac, aq, precision, steps) + } + + // dec_sin(a) = b + function dec_sin(ac, aq, precision, steps) -> bc, bq { + bc, bq := verbatim_4i_2o(hex"d6", ac, aq, precision, steps) + } + + // derived functions + + // a - b = c + function dec_sub(ac, aq, bc, bq, precision) -> cc, cq { + cc, cq := dec_neg(bc, bq) + cc, cq := dec_add(ac, aq, cc, cq, precision) + } + + // a / b = c + function dec_div(ac, aq, bc, bq, precision) -> cc, cq { + cc, cq := dec_inv(bc, bq, precision) + cc, cq := dec_mul(ac, aq, cc, cq, precision) + } + + // a^b = dec_exp(b * dec_ln(a)) + function pow(ac, aq, bc, bq, precision, steps) -> cc, cq { + cc, cq := dec_ln(ac, aq, precision, steps) + cc, cq := dec_mul(bc, bq, cc, cq, precision) + cc, cq := dec_exp(cc, cq, precision, steps) + } + + // dec_sqrt(a) = a^(1/2) + function dec_sqrt(ac, aq, precision, steps) -> bc, bq { + let MINUS_ONE := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // -1 + bc, bq := pow(ac, aq, 5, MINUS_ONE, precision, steps) + } + + // dec_sqr(a) = a^2 + function dec_sqr(ac, aq, precision) -> bc, bq { + bc, bq := dec_mul(ac, aq, ac, aq, precision) + } + } + } +} diff --git a/assets/eip-EVM+/Neuron.yul b/assets/eip-EVM+/Neuron.yul new file mode 100644 index 00000000000000..ffe1fe4f4fcaa4 --- /dev/null +++ b/assets/eip-EVM+/Neuron.yul @@ -0,0 +1,369 @@ +// solc --evm-version london --strict-assembly Neuron.yul >> Neuron.txt + +// user can set_weights any number of (decimal) weights +// user can run the Neuron, outputting sigmoid of the weighted sum of inputs + +object "Neuron" { + code { + datacopy(0, dataoffset("runtime"), datasize("runtime")) + return(0, datasize("runtime")) + } + object "runtime" { + code { + // a = ac*10^aq is a decimal + // ac, aq int256 as 2's complement + + // Dispatcher + switch selector() + + // set_weights(int256[]) + case 0x61d311e8 { + let num_weights := div(calldatasize(), 64) // should be even // 64 since two words make one decimal + for { let i := 0 } lt(i, num_weights) { i := add(i, 1) } + { + let memory_address := add(mul(i, 64), 4) + let weight_c := calldataload(memory_address) + let index_address := mul(i, 2) + sstore(index_address, weight_c) + + memory_address := add(memory_address, 32) + let weight_q := calldataload(memory_address) + index_address := add(index_address, 1) + sstore(index_address, weight_q) + } + } + + // will use as many input weights as input supplied + // first two inputs are precision and steps + // run(int256[]) + case 0xc5b5bb77 { + let precision := calldataload(4) + let steps := calldataload(36) + + let num_inputs_times_64 := sub(calldatasize(), 68) // expect full word per weight // 64 since two words make one decimal // 68: 4 for function selector + calldatacopy(0, 68, num_inputs_times_64) // inputs + + let num_inputs := div(num_inputs_times_64, 64) // expect full word per weight // 64 since two words make one decimal // 68: 4 for function selector + let yc, yq := neuron(num_inputs, precision, steps) + + mstore(0, yc) + mstore(32, yq) + + //debug + // sstore(12, num_inputs) + // sstore(13, precision) + // sstore(14, steps) + // sstore(15, yc) + // sstore(16, yq) + log0(0, 32) // yc + log0(32, 32) // yq + //debug + + return(0, 64) + } + default { + revert(0, 0) + } + function selector() -> s { + s := div(calldataload(0), 0x100000000000000000000000000000000000000000000000000000000) + } + + // Neuron with sigmoid activation + // https://en.wikipedia.org/wiki/Artificial_neuron + + function neuron(num_inputs, precision, steps) -> yc, yq { + let xc, xq := weighted_sum(num_inputs, precision, steps) + yc, yq := sigmoid(xc, xq, precision, steps) + + //debug + // sstore(15, xc) + // sstore(16, xq) + // sstore(17, yc) + // sstore(18, yq) + //debug + + } + + function weighted_sum(num_inputs, precision, steps) -> total_c, total_q { + total_c := 0 + total_q := 0 + + for { let i := 0 } lt(i, num_inputs) { i := add(i, 1) } + { + let index_address := mul(i, 2) + let weight_c := sload(index_address) + index_address := add(index_address, 1) + let weight_q := sload(index_address) + + let memory_address := mul(i, 64) + let input_c := mload(memory_address) + memory_address := add(memory_address, 32) + let input_q := mload(memory_address) + + let product_c, product_q := dec_mul(weight_c, weight_q, input_c, input_q, precision) + + total_c, total_q := dec_add(total_c, total_q, product_c, product_q, precision) + + //debug + // sstore(add(mul(i,10),30), index_address) + // sstore(add(mul(i,10),31), memory_address) + // sstore(add(mul(i,10),32), input_c) + // sstore(add(mul(i,10),33), input_q) + // sstore(add(mul(i,10),34), weight_c) + // sstore(add(mul(i,10),35), weight_q) + // sstore(add(mul(i,10),36), product_c) + // sstore(add(mul(i,10),37), product_q) + // sstore(add(mul(i,10),38), total_c) + // sstore(add(mul(i,10),39), total_q) + //debug + } + } + + function sigmoid(xc, xq, precision, steps) -> yc, yq { + let mxc, mxq := dec_neg(xc, xq) // -x + let emxc, emxq := dec_exp(mxc, mxq, precision, steps) // exp(-x) + let oemxc, oemxq := dec_add(1, 0, emxc, emxq, precision) // 1 + exp(-x) + yc, yq := dec_inv(oemxc, oemxq, precision) // (1 + exp(-x))^(-1) = sigmoid(x) + } + + + // OPCODE -> function + + // a + b = c + function dec_add(ac, aq, bc, bq, precision) -> cc, cq { + cc, cq := verbatim_5i_2o(hex"d0", ac, aq, bc, bq, precision) + } + + // -a = b + function dec_neg(ac, aq) -> bc, bq { + bc, bq := verbatim_2i_2o(hex"d1", ac, aq) + } + + // a * b = c + function dec_mul(ac, aq, bc, bq, precision) -> cc, cq { + cc, cq := verbatim_5i_2o(hex"d2", ac, aq, bc, bq, precision) + } + + // 1 / a = b + function dec_inv(ac, aq, precision) -> bc, bq { + bc, bq := verbatim_3i_2o(hex"d3", ac, aq, precision) + } + + // dec_exp(a) = b + function dec_exp(ac, aq, precision, steps) -> bc, bq { + bc, bq := verbatim_4i_2o(hex"d4", ac, aq, precision, steps) + } + + // dec_ln(a) = b + function dec_ln(ac, aq, precision, steps) -> bc, bq { + bc, bq := verbatim_4i_2o(hex"d5", ac, aq, precision, steps) + } + + // dec_sin(a) = b + function dec_sin(ac, aq, precision, steps) -> bc, bq { + bc, bq := verbatim_4i_2o(hex"d6", ac, aq, precision, steps) + } + + // derived functions + + // a - b = c + function dec_sub(ac, aq, bc, bq, precision) -> cc, cq { + cc, cq := dec_neg(bc, bq) + cc, cq := dec_add(ac, aq, cc, cq, precision) + } + + // a / b = c + function dec_div(ac, aq, bc, bq, precision) -> cc, cq { + cc, cq := dec_inv(bc, bq, precision) + cc, cq := dec_mul(ac, aq, cc, cq, precision) + } + + // a^b = dec_exp(b * dec_ln(a)) + function pow(ac, aq, bc, bq, precision, steps) -> cc, cq { + cc, cq := dec_ln(ac, aq, precision, steps) + cc, cq := dec_mul(bc, bq, cc, cq, precision) + cc, cq := dec_exp(cc, cq, precision, steps) + } + + // dec_sqrt(a) = a^(1/2) + function dec_sqrt(ac, aq, precision, steps) -> bc, bq { + let MINUS_ONE := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // -1 + bc, bq := pow(ac, aq, 5, MINUS_ONE, precision, steps) + } + + // dec_sqr(a) = a^2 + function dec_sqr(ac, aq, precision) -> bc, bq { + bc, bq := dec_mul(ac, aq, ac, aq, precision) + } + } + } +} + + +// // BlackScholes +// // S: underlying price +// // K: strike +// // r: interest +// // s: volatility +// // T: time + +// function r_s2_T() { +// let precision := mload(320) + +// let sc := mload(192) +// let sq := mload(224) +// let s_sqr_c, s_sqr_q := dec_sqr(sc, sq, precision) + +// let sigma_sqr_half_c, sigma_sqr_half_q := dec_div(s_sqr_c, s_sqr_q, 2, 0, precision) + +// let rc := mload(128) +// let rq := mload(160) +// let r_p_s_c, r_p_s_q := dec_add(sigma_sqr_half_c, sigma_sqr_half_q, rc, rq, precision) + +// let Tc := mload(256) +// let Tq := mload(288) +// let r_s2_T_c, r_s2_T_q := dec_mul(r_p_s_c, r_p_s_q, Tc, Tq, precision) + +// mstore(384, r_s2_T_c) +// mstore(416, r_s2_T_q) +// } + +// function ln_S_K() { +// let precision := mload(320) +// let steps := mload(352) + +// let Sc := mload(0) +// let Sq := mload(32) +// let Kc := mload(64) +// let Kq := mload(96) +// let S_K_c, S_K_q := dec_div(Sc, Sq, Kc, Kq, precision) +// let ln_S_K_c, ln_S_K_q := dec_ln(S_K_c, S_K_q, precision, steps) + +// mstore(448, ln_S_K_c) +// mstore(480, ln_S_K_q) +// } +// function d_plus_s_T() { +// let precision := mload(320) +// let steps := mload(352) + +// let sc := mload(192) +// let sq := mload(224) +// let Tc := mload(256) +// let Tq := mload(288) +// let sqrt_T_c, sqrt_T_q := dec_sqrt(Tc, Tq, precision, steps) +// let s_sqrt_T_c, s_sqrt_T_q := dec_mul(sc, sq, sqrt_T_c, sqrt_T_q, precision) + +// mstore(384, s_sqrt_T_c) +// mstore(416, s_sqrt_T_q) +// } +// function d_plus() { +// let precision := mload(320) +// let steps := mload(352) + +// r_s2_T() +// let r_s2_T_c := mload(384) +// let r_s2_T_q := mload(416) +// ln_S_K() +// let ln_S_K_c := mload(448) +// let ln_S_K_q := mload(480) +// let ln_S_K_p_r_s2_T_c, ln_S_K_p_r_s2_T_q := dec_add(ln_S_K_c,ln_S_K_q, r_s2_T_c, r_s2_T_q, precision) + +// d_plus_s_T() +// let s_sqrt_T_c := mload(384) +// let s_sqrt_T_q := mload(416) + +// let d_plus_c, d_plus_q := dec_div(ln_S_K_p_r_s2_T_c, ln_S_K_p_r_s2_T_q, s_sqrt_T_c, s_sqrt_T_q, precision) + +// mstore(448, d_plus_c) +// mstore(480, d_plus_q) +// } +// function d_minus() { +// let precision := mload(320) + +// let d_plus_c := mload(448) +// let d_plus_q := mload(480) +// let s_sqrt_T_c := mload(384) +// let s_sqrt_T_q := mload(416) +// let d_minus_c, d_minus_q := dec_sub(d_plus_c, d_plus_q, s_sqrt_T_c, s_sqrt_T_q, precision) + +// mstore(384, d_minus_c) +// mstore(416, d_minus_q) +// } +// // approximation +// // 1/(1+dec_exp(-1.65451*a)) +// function CDF(ac, aq) -> bc, bq { +// let precision := mload(320) +// let steps := mload(352) + +// let C := 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd79b5 // -165451 +// let MINUS_FIVE := 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb // -5 + +// let b1_c, b1_q := dec_mul(C, MINUS_FIVE, ac, aq, precision) +// let b2_c, b2_q := dec_exp(b1_c, b1_q, precision, steps) +// let b3_c, b3_q := dec_add(b2_c, b2_q, 1, 0, precision) +// bc, bq := dec_inv(b3_c, b3_q, precision) +// } +// function cdf_dp_S() { +// let precision := mload(320) + +// let d_plus_c := mload(448) +// let d_plus_q := mload(480) +// let cdf_dp_c, cdf_dp_q := CDF(d_plus_c, d_plus_q) + +// let Sc := mload(0) +// let Sq := mload(32) +// let cdf_dp_S_c, cdf_dp_S_q := dec_mul(Sc, Sq, cdf_dp_c, cdf_dp_q, precision) + +// mstore(448, cdf_dp_S_c) +// mstore(480, cdf_dp_S_q) +// } +// function cdf_dm_K_exp_r_T() { +// let precision := mload(320) +// let steps := mload(352) + +// let rc := mload(128) +// let rq := mload(160) +// let r_n_c, r_n_q := dec_neg(rc, rq) +// let Tc := mload(256) +// let Tq := mload(288) +// let r_T_c, r_T_q := dec_mul(r_n_c, r_n_q, Tc, Tq, precision) +// let exp_r_T_c, exp_r_T_q := dec_exp(r_T_c, r_T_q, precision, steps) +// let Kc := mload(64) +// let Kq := mload(96) +// let K_exp_r_T_c, K_exp_r_T_q := dec_mul(Kc, Kq, exp_r_T_c, exp_r_T_q, precision) + +// mstore(384, K_exp_r_T_c) +// mstore(416, K_exp_r_T_q) +// } +// function cdf_dm_K() { +// let precision := mload(320) +// let steps := mload(352) + +// let d_minus_c := mload(384) +// let d_minus_q := mload(416) + +// cdf_dm_K_exp_r_T() +// let K_exp_r_T_c := mload(384) +// let K_exp_r_T_q := mload(416) + +// let cdf_dm_c, cdf_dm_q := CDF(d_minus_c, d_minus_q) +// let cdf_dm_K_c, cdf_dm_K_q := dec_mul(cdf_dm_c, cdf_dm_q, K_exp_r_T_c, K_exp_r_T_q, precision) + +// mstore(384, cdf_dm_K_c) +// mstore(416, cdf_dm_K_q) +// } +// function callprice() -> ac, aq { +// let precision := mload(320) + +// d_plus() +// d_minus() +// cdf_dp_S() +// cdf_dm_K() + +// let cdf_dm_K_c := mload(384) +// let cdf_dm_K_q := mload(416) +// let cdf_dp_S_c := mload(448) +// let cdf_dp_S_q := mload(480) + +// ac, aq := dec_sub(cdf_dp_S_c, cdf_dp_S_q, cdf_dm_K_c, cdf_dm_K_q, precision) +// } \ No newline at end of file From a83fddb1d1cea148bcec724e0d5fe29c3d80d33a Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Sun, 22 Oct 2023 19:48:17 -0700 Subject: [PATCH 16/39] Update and rename eip-EVM+.md to eip-7543.md change title, non branded, short and descriptive --- EIPS/{eip-EVM+.md => eip-7543.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename EIPS/{eip-EVM+.md => eip-7543.md} (99%) diff --git a/EIPS/eip-EVM+.md b/EIPS/eip-7543.md similarity index 99% rename from EIPS/eip-EVM+.md rename to EIPS/eip-7543.md index de6dd1fc26d3e8..f6c43054871ad2 100644 --- a/EIPS/eip-EVM+.md +++ b/EIPS/eip-7543.md @@ -1,6 +1,6 @@ --- eip: 7543 -title: EVM+: Decimal math in the EVM +title: EVM decimal math description: OPCODEs to allow high precision decimal calculation of all elementary functions. author: 1m1 (@1m1-github) discussions-to: https://ethereum-magicians.org/t/decimal-math-on-evm/16194 From 95458c58538d34988d9d7df1199e861cf9181123 Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Sun, 22 Oct 2023 19:50:39 -0700 Subject: [PATCH 17/39] remove debug code --- assets/eip-EVM+/Neuron.yul | 199 +------------------------------------ 1 file changed, 1 insertion(+), 198 deletions(-) diff --git a/assets/eip-EVM+/Neuron.yul b/assets/eip-EVM+/Neuron.yul index ffe1fe4f4fcaa4..b2ea6afd47a6ea 100644 --- a/assets/eip-EVM+/Neuron.yul +++ b/assets/eip-EVM+/Neuron.yul @@ -49,15 +49,8 @@ object "Neuron" { mstore(0, yc) mstore(32, yq) - //debug - // sstore(12, num_inputs) - // sstore(13, precision) - // sstore(14, steps) - // sstore(15, yc) - // sstore(16, yq) log0(0, 32) // yc log0(32, 32) // yq - //debug return(0, 64) } @@ -73,15 +66,7 @@ object "Neuron" { function neuron(num_inputs, precision, steps) -> yc, yq { let xc, xq := weighted_sum(num_inputs, precision, steps) - yc, yq := sigmoid(xc, xq, precision, steps) - - //debug - // sstore(15, xc) - // sstore(16, xq) - // sstore(17, yc) - // sstore(18, yq) - //debug - + yc, yq := sigmoid(xc, xq, precision, steps) } function weighted_sum(num_inputs, precision, steps) -> total_c, total_q { @@ -103,19 +88,6 @@ object "Neuron" { let product_c, product_q := dec_mul(weight_c, weight_q, input_c, input_q, precision) total_c, total_q := dec_add(total_c, total_q, product_c, product_q, precision) - - //debug - // sstore(add(mul(i,10),30), index_address) - // sstore(add(mul(i,10),31), memory_address) - // sstore(add(mul(i,10),32), input_c) - // sstore(add(mul(i,10),33), input_q) - // sstore(add(mul(i,10),34), weight_c) - // sstore(add(mul(i,10),35), weight_q) - // sstore(add(mul(i,10),36), product_c) - // sstore(add(mul(i,10),37), product_q) - // sstore(add(mul(i,10),38), total_c) - // sstore(add(mul(i,10),39), total_q) - //debug } } @@ -198,172 +170,3 @@ object "Neuron" { } } } - - -// // BlackScholes -// // S: underlying price -// // K: strike -// // r: interest -// // s: volatility -// // T: time - -// function r_s2_T() { -// let precision := mload(320) - -// let sc := mload(192) -// let sq := mload(224) -// let s_sqr_c, s_sqr_q := dec_sqr(sc, sq, precision) - -// let sigma_sqr_half_c, sigma_sqr_half_q := dec_div(s_sqr_c, s_sqr_q, 2, 0, precision) - -// let rc := mload(128) -// let rq := mload(160) -// let r_p_s_c, r_p_s_q := dec_add(sigma_sqr_half_c, sigma_sqr_half_q, rc, rq, precision) - -// let Tc := mload(256) -// let Tq := mload(288) -// let r_s2_T_c, r_s2_T_q := dec_mul(r_p_s_c, r_p_s_q, Tc, Tq, precision) - -// mstore(384, r_s2_T_c) -// mstore(416, r_s2_T_q) -// } - -// function ln_S_K() { -// let precision := mload(320) -// let steps := mload(352) - -// let Sc := mload(0) -// let Sq := mload(32) -// let Kc := mload(64) -// let Kq := mload(96) -// let S_K_c, S_K_q := dec_div(Sc, Sq, Kc, Kq, precision) -// let ln_S_K_c, ln_S_K_q := dec_ln(S_K_c, S_K_q, precision, steps) - -// mstore(448, ln_S_K_c) -// mstore(480, ln_S_K_q) -// } -// function d_plus_s_T() { -// let precision := mload(320) -// let steps := mload(352) - -// let sc := mload(192) -// let sq := mload(224) -// let Tc := mload(256) -// let Tq := mload(288) -// let sqrt_T_c, sqrt_T_q := dec_sqrt(Tc, Tq, precision, steps) -// let s_sqrt_T_c, s_sqrt_T_q := dec_mul(sc, sq, sqrt_T_c, sqrt_T_q, precision) - -// mstore(384, s_sqrt_T_c) -// mstore(416, s_sqrt_T_q) -// } -// function d_plus() { -// let precision := mload(320) -// let steps := mload(352) - -// r_s2_T() -// let r_s2_T_c := mload(384) -// let r_s2_T_q := mload(416) -// ln_S_K() -// let ln_S_K_c := mload(448) -// let ln_S_K_q := mload(480) -// let ln_S_K_p_r_s2_T_c, ln_S_K_p_r_s2_T_q := dec_add(ln_S_K_c,ln_S_K_q, r_s2_T_c, r_s2_T_q, precision) - -// d_plus_s_T() -// let s_sqrt_T_c := mload(384) -// let s_sqrt_T_q := mload(416) - -// let d_plus_c, d_plus_q := dec_div(ln_S_K_p_r_s2_T_c, ln_S_K_p_r_s2_T_q, s_sqrt_T_c, s_sqrt_T_q, precision) - -// mstore(448, d_plus_c) -// mstore(480, d_plus_q) -// } -// function d_minus() { -// let precision := mload(320) - -// let d_plus_c := mload(448) -// let d_plus_q := mload(480) -// let s_sqrt_T_c := mload(384) -// let s_sqrt_T_q := mload(416) -// let d_minus_c, d_minus_q := dec_sub(d_plus_c, d_plus_q, s_sqrt_T_c, s_sqrt_T_q, precision) - -// mstore(384, d_minus_c) -// mstore(416, d_minus_q) -// } -// // approximation -// // 1/(1+dec_exp(-1.65451*a)) -// function CDF(ac, aq) -> bc, bq { -// let precision := mload(320) -// let steps := mload(352) - -// let C := 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd79b5 // -165451 -// let MINUS_FIVE := 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb // -5 - -// let b1_c, b1_q := dec_mul(C, MINUS_FIVE, ac, aq, precision) -// let b2_c, b2_q := dec_exp(b1_c, b1_q, precision, steps) -// let b3_c, b3_q := dec_add(b2_c, b2_q, 1, 0, precision) -// bc, bq := dec_inv(b3_c, b3_q, precision) -// } -// function cdf_dp_S() { -// let precision := mload(320) - -// let d_plus_c := mload(448) -// let d_plus_q := mload(480) -// let cdf_dp_c, cdf_dp_q := CDF(d_plus_c, d_plus_q) - -// let Sc := mload(0) -// let Sq := mload(32) -// let cdf_dp_S_c, cdf_dp_S_q := dec_mul(Sc, Sq, cdf_dp_c, cdf_dp_q, precision) - -// mstore(448, cdf_dp_S_c) -// mstore(480, cdf_dp_S_q) -// } -// function cdf_dm_K_exp_r_T() { -// let precision := mload(320) -// let steps := mload(352) - -// let rc := mload(128) -// let rq := mload(160) -// let r_n_c, r_n_q := dec_neg(rc, rq) -// let Tc := mload(256) -// let Tq := mload(288) -// let r_T_c, r_T_q := dec_mul(r_n_c, r_n_q, Tc, Tq, precision) -// let exp_r_T_c, exp_r_T_q := dec_exp(r_T_c, r_T_q, precision, steps) -// let Kc := mload(64) -// let Kq := mload(96) -// let K_exp_r_T_c, K_exp_r_T_q := dec_mul(Kc, Kq, exp_r_T_c, exp_r_T_q, precision) - -// mstore(384, K_exp_r_T_c) -// mstore(416, K_exp_r_T_q) -// } -// function cdf_dm_K() { -// let precision := mload(320) -// let steps := mload(352) - -// let d_minus_c := mload(384) -// let d_minus_q := mload(416) - -// cdf_dm_K_exp_r_T() -// let K_exp_r_T_c := mload(384) -// let K_exp_r_T_q := mload(416) - -// let cdf_dm_c, cdf_dm_q := CDF(d_minus_c, d_minus_q) -// let cdf_dm_K_c, cdf_dm_K_q := dec_mul(cdf_dm_c, cdf_dm_q, K_exp_r_T_c, K_exp_r_T_q, precision) - -// mstore(384, cdf_dm_K_c) -// mstore(416, cdf_dm_K_q) -// } -// function callprice() -> ac, aq { -// let precision := mload(320) - -// d_plus() -// d_minus() -// cdf_dp_S() -// cdf_dm_K() - -// let cdf_dm_K_c := mload(384) -// let cdf_dm_K_q := mload(416) -// let cdf_dp_S_c := mload(448) -// let cdf_dp_S_q := mload(480) - -// ac, aq := dec_sub(cdf_dp_S_c, cdf_dp_S_q, cdf_dm_K_c, cdf_dm_K_q, precision) -// } \ No newline at end of file From b0ce024468bc150b85d4ef05e6dcd723cbdf2986 Mon Sep 17 00:00:00 2001 From: 1m1 Date: Sun, 22 Oct 2023 19:56:51 -0700 Subject: [PATCH 18/39] rename assets/folder --- assets/{eip-EVM+ => eip-7543}/BlackScholes.yul | 0 assets/{eip-EVM+ => eip-7543}/Neuron.yul | 0 assets/{eip-EVM+ => eip-7543}/decimal_fixed.go | 0 assets/{eip-EVM+ => eip-7543}/decimal_fixed_test.go | 0 assets/{eip-EVM+ => eip-7543}/gasEVMPlusEmulate.go | 0 assets/{eip-EVM+ => eip-7543}/uint256_wrapped.go | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename assets/{eip-EVM+ => eip-7543}/BlackScholes.yul (100%) rename assets/{eip-EVM+ => eip-7543}/Neuron.yul (100%) rename assets/{eip-EVM+ => eip-7543}/decimal_fixed.go (100%) rename assets/{eip-EVM+ => eip-7543}/decimal_fixed_test.go (100%) rename assets/{eip-EVM+ => eip-7543}/gasEVMPlusEmulate.go (100%) rename assets/{eip-EVM+ => eip-7543}/uint256_wrapped.go (100%) diff --git a/assets/eip-EVM+/BlackScholes.yul b/assets/eip-7543/BlackScholes.yul similarity index 100% rename from assets/eip-EVM+/BlackScholes.yul rename to assets/eip-7543/BlackScholes.yul diff --git a/assets/eip-EVM+/Neuron.yul b/assets/eip-7543/Neuron.yul similarity index 100% rename from assets/eip-EVM+/Neuron.yul rename to assets/eip-7543/Neuron.yul diff --git a/assets/eip-EVM+/decimal_fixed.go b/assets/eip-7543/decimal_fixed.go similarity index 100% rename from assets/eip-EVM+/decimal_fixed.go rename to assets/eip-7543/decimal_fixed.go diff --git a/assets/eip-EVM+/decimal_fixed_test.go b/assets/eip-7543/decimal_fixed_test.go similarity index 100% rename from assets/eip-EVM+/decimal_fixed_test.go rename to assets/eip-7543/decimal_fixed_test.go diff --git a/assets/eip-EVM+/gasEVMPlusEmulate.go b/assets/eip-7543/gasEVMPlusEmulate.go similarity index 100% rename from assets/eip-EVM+/gasEVMPlusEmulate.go rename to assets/eip-7543/gasEVMPlusEmulate.go diff --git a/assets/eip-EVM+/uint256_wrapped.go b/assets/eip-7543/uint256_wrapped.go similarity index 100% rename from assets/eip-EVM+/uint256_wrapped.go rename to assets/eip-7543/uint256_wrapped.go From a9752fcbc83c0b8005b7f838c3dbcc974c18fc6c Mon Sep 17 00:00:00 2001 From: 1m1 Date: Sun, 22 Oct 2023 19:59:34 -0700 Subject: [PATCH 19/39] BlackScholes,Neuron --- EIPS/eip-7543.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/EIPS/eip-7543.md b/EIPS/eip-7543.md index f6c43054871ad2..704b0a54f7b677 100644 --- a/EIPS/eip-7543.md +++ b/EIPS/eip-7543.md @@ -22,6 +22,8 @@ The simplest task in trading e.g. is to convert volatilities from yearly to dail Giving users/devs the same ability that scientific calculators have allows for the creation of apps with higher complexity. +The files ../assets/eip-7543/BlackScholes.yul and ../assets/eip-7543/Neuron.yul demonstrate how these OPCODEs simplify numerical smart contract code. + ### Why decimal? A simple value like 0.1 cannot be represented finitely in binary. Decimal types are much closer to the vast majority of numerical calculations run by humans. From ea7c9f355e547ff870fc5021cd1eb0a6967f0b32 Mon Sep 17 00:00:00 2001 From: 1m1 Date: Sun, 22 Oct 2023 20:04:28 -0700 Subject: [PATCH 20/39] Error: EIPS/eip-7543.md:27 MD022/blanks-around-headings/blanks-around-headers Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] [Context: "### Why decimal?"] Error: Failed with exit code: 1 --- EIPS/eip-7543.md | 1 + 1 file changed, 1 insertion(+) diff --git a/EIPS/eip-7543.md b/EIPS/eip-7543.md index 704b0a54f7b677..29f52a68abf44b 100644 --- a/EIPS/eip-7543.md +++ b/EIPS/eip-7543.md @@ -25,6 +25,7 @@ Giving users/devs the same ability that scientific calculators have allows for t The files ../assets/eip-7543/BlackScholes.yul and ../assets/eip-7543/Neuron.yul demonstrate how these OPCODEs simplify numerical smart contract code. ### Why decimal? + A simple value like 0.1 cannot be represented finitely in binary. Decimal types are much closer to the vast majority of numerical calculations run by humans. ### eVm From 1a421fbf504c2148a5d343002645affb9a69e8bb Mon Sep 17 00:00:00 2001 From: 1m1 Date: Sun, 22 Oct 2023 20:14:51 -0700 Subject: [PATCH 21/39] like this? --- EIPS/eip-7543.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7543.md b/EIPS/eip-7543.md index 29f52a68abf44b..1ed19c2749af5d 100644 --- a/EIPS/eip-7543.md +++ b/EIPS/eip-7543.md @@ -22,7 +22,7 @@ The simplest task in trading e.g. is to convert volatilities from yearly to dail Giving users/devs the same ability that scientific calculators have allows for the creation of apps with higher complexity. -The files ../assets/eip-7543/BlackScholes.yul and ../assets/eip-7543/Neuron.yul demonstrate how these OPCODEs simplify numerical smart contract code. +The files [EIP-7543](../assets/eip-7543/BlackScholes.yul) and [EIP-7543](../assets/eip-7543/Neuron.yul) demonstrate how these OPCODEs simplify numerical smart contract code. ### Why decimal? From 97edf3e28f8f05fa3d790c3b88e26947aa50ed63 Mon Sep 17 00:00:00 2001 From: 1m1 Date: Mon, 23 Oct 2023 17:55:16 -0700 Subject: [PATCH 22/39] title, description more precise --- EIPS/eip-7543.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-7543.md b/EIPS/eip-7543.md index 1ed19c2749af5d..170958cd9a0f3e 100644 --- a/EIPS/eip-7543.md +++ b/EIPS/eip-7543.md @@ -1,7 +1,7 @@ --- eip: 7543 -title: EVM decimal math -description: OPCODEs to allow high precision decimal calculation of all elementary functions. +title: EVM decimal float elementary functions +description: This EIP adds OPCODEs to allow high precision decimal float calculation of all elementary functions with precise gas enumeration. author: 1m1 (@1m1-github) discussions-to: https://ethereum-magicians.org/t/decimal-math-on-evm/16194 status: Draft From fcbba1ff6785148d351e3eb4b367ff75e89ff785 Mon Sep 17 00:00:00 2001 From: 1m1 Date: Mon, 23 Oct 2023 17:57:48 -0700 Subject: [PATCH 23/39] it is actually a floating type Abstract --- EIPS/eip-7543.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7543.md b/EIPS/eip-7543.md index 170958cd9a0f3e..d49cf2a14c0c69 100644 --- a/EIPS/eip-7543.md +++ b/EIPS/eip-7543.md @@ -13,7 +13,7 @@ created: 2023-10-22 ## Abstract -This EIP adds *decimal fixed* OPCODEs for arithmetic (DECADD, DECNEG, DECMUL, DECINV) and expression of all elementary functions (DECEXP, DECLN, DECSIN). All decimal values upto the maximal precision allowed by a int256 coefficient and exponent are represented exactly, as c*10^q. All implemented algorithms converge for all inputs given enough precision, as chosen by the user. All calculations are deterministic and gas is embedded bottom-up. Allowing high precision decimal elementary functions invites the worlds of mathematical finance, machine learning, science, digital art, games and others to Ethereum. The implementation is functional. +This EIP adds *decimal float* OPCODEs for arithmetic via DECADD, DECNEG, DECMUL, DECINV and expression of all elementary functions via DECEXP, DECLN, DECSIN. All decimal values upto the maximal precision allowed by a int256 coefficient and exponent are represented exactly, as c*10^q. All implemented algorithms converge for all inputs given enough precision, as chosen by the user. All calculations are deterministic and gas is precisely embedded bottom-up. Allowing high precision decimal elementary functions invites the worlds of mathematical finance, machine learning, science, digital art, games and others to Ethereum. The implementation is functional. ## Motivation From 413bfa9a62df15f62648db4deecdadd9d1701517 Mon Sep 17 00:00:00 2001 From: 1m1 Date: Mon, 23 Oct 2023 18:01:14 -0700 Subject: [PATCH 24/39] Backwards Compatibility --- EIPS/eip-7543.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/EIPS/eip-7543.md b/EIPS/eip-7543.md index d49cf2a14c0c69..55915ade6e5f69 100644 --- a/EIPS/eip-7543.md +++ b/EIPS/eip-7543.md @@ -139,6 +139,8 @@ since the costs depend on the input, a fuzzing would give us close to the worst No backward compatibility issues found. +Since the EVM always contracts to deploy with invalid code, there could be previously invalid code that becomes valid when adding a new OPCODE. The EVM could be designed to expect a version tag at the beginning of all bytecode or (not xor) to only deploy valid code. + ## Test Cases ../assets/eip-EVM+/decimal_fixed_test.go From 2aead6ee872206a86eec36000b88fa41ff3efbb1 Mon Sep 17 00:00:00 2001 From: 1m1 Date: Mon, 23 Oct 2023 18:16:37 -0700 Subject: [PATCH 25/39] this would be better --- EIPS/eip-7543.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7543.md b/EIPS/eip-7543.md index 55915ade6e5f69..b2072c7f37e884 100644 --- a/EIPS/eip-7543.md +++ b/EIPS/eip-7543.md @@ -22,7 +22,7 @@ The simplest task in trading e.g. is to convert volatilities from yearly to dail Giving users/devs the same ability that scientific calculators have allows for the creation of apps with higher complexity. -The files [EIP-7543](../assets/eip-7543/BlackScholes.yul) and [EIP-7543](../assets/eip-7543/Neuron.yul) demonstrate how these OPCODEs simplify numerical smart contract code. +The files [BlackScholes.yul](../assets/eip-7543/BlackScholes.yul) and [Neuron.yul](../assets/eip-7543/Neuron.yul) demonstrate how these OPCODEs simplify numerical smart contract code. ### Why decimal? From 4fee997cf5a34478358ce39fda4102119a858b71 Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Mon, 23 Oct 2023 20:01:20 -0700 Subject: [PATCH 26/39] local -> pure closer to the technical term from CS --- EIPS/eip-7543.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7543.md b/EIPS/eip-7543.md index b2072c7f37e884..b2a2884ec06925 100644 --- a/EIPS/eip-7543.md +++ b/EIPS/eip-7543.md @@ -131,7 +131,7 @@ because the EVM interprator expects the gas cost before actually running the OPC this gives an embedded gas calcuation, which works well for complex OPCODEs (see gasEVMPlusEmulate in ../assets/eip-EVM+/gasEVMPlusEmulate.go). -to remove the double gas, a future EIP would suggest the following: allow contract code to run whilst accumulating gas (at runtime) and panicking in case of limit breach, without requiring the cost in advance. this only works for contract code that is local, defined as code that only depends on the user input and the inner bytecode of the contract. local contracts cannot use state from the chain, nor make calls to other contracts. pure mathematical functions would e.g. be local contracts. local contracts are fully deterministic given the input, allowing a user to estimate gas costs offline (cheaper) and the EVM to panic at runtime, without knowing gas in advance. +to remove the double gas, a future EIP would suggest the following: allow contract code to run whilst accumulating gas (at runtime) and panicking in case of limit breach, without requiring the cost in advance. this only works for contract code that is pure, defined as code that only depends on the user input and the inner bytecode of the contract. pure contracts cannot use state from the chain, nor make calls to other contracts. pure mathematical functions would e.g. be pure contracts. pure contracts are fully deterministic given the input, allowing a user to estimate gas costs offline (cheaper) and the EVM to panic at runtime, without knowing gas in advance. since the costs depend on the input, a fuzzing would give us close to the worst cases (TODO). From ea133ab2f7962f83392901fc249fa9168d5c3f5b Mon Sep 17 00:00:00 2001 From: 1m1 Date: Tue, 24 Oct 2023 08:54:27 -0700 Subject: [PATCH 27/39] title should be easy to remember ... description contains details --- EIPS/eip-7543.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7543.md b/EIPS/eip-7543.md index b2a2884ec06925..5c87001d923096 100644 --- a/EIPS/eip-7543.md +++ b/EIPS/eip-7543.md @@ -1,6 +1,6 @@ --- eip: 7543 -title: EVM decimal float elementary functions +title: EVM decimal math description: This EIP adds OPCODEs to allow high precision decimal float calculation of all elementary functions with precise gas enumeration. author: 1m1 (@1m1-github) discussions-to: https://ethereum-magicians.org/t/decimal-math-on-evm/16194 From faac185dd003f76f4ddef61ab637b7abe33def35 Mon Sep 17 00:00:00 2001 From: 1m1 Date: Tue, 24 Oct 2023 09:04:39 -0700 Subject: [PATCH 28/39] fixed->float --- EIPS/eip-7543.md | 4 ++-- assets/eip-7543/{decimal_fixed.go => decimal_float.go} | 0 .../eip-7543/{decimal_fixed_test.go => decimal_float_test.go} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename assets/eip-7543/{decimal_fixed.go => decimal_float.go} (100%) rename assets/eip-7543/{decimal_fixed_test.go => decimal_float_test.go} (100%) diff --git a/EIPS/eip-7543.md b/EIPS/eip-7543.md index 5c87001d923096..446c33700b1fc9 100644 --- a/EIPS/eip-7543.md +++ b/EIPS/eip-7543.md @@ -143,11 +143,11 @@ Since the EVM always contracts to deploy with invalid code, there could be previ ## Test Cases -../assets/eip-EVM+/decimal_fixed_test.go +../assets/eip-EVM+/decimal_float_test.go ## Reference Implementation -The reference implementation is found in ../assets/eip-EVM+/decimal_fixed.go +The reference implementation is found in ../assets/eip-EVM+/decimal_float.go ## Security Considerations diff --git a/assets/eip-7543/decimal_fixed.go b/assets/eip-7543/decimal_float.go similarity index 100% rename from assets/eip-7543/decimal_fixed.go rename to assets/eip-7543/decimal_float.go diff --git a/assets/eip-7543/decimal_fixed_test.go b/assets/eip-7543/decimal_float_test.go similarity index 100% rename from assets/eip-7543/decimal_fixed_test.go rename to assets/eip-7543/decimal_float_test.go From b2b48e913ad9fbb9c52a0f4553fedc65623d98c5 Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Fri, 3 Nov 2023 15:11:58 -0700 Subject: [PATCH 29/39] update...small improvements --- assets/eip-7543/decimal_float.go | 37 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/assets/eip-7543/decimal_float.go b/assets/eip-7543/decimal_float.go index 4568b334e7508f..69aee82728dbc0 100644 --- a/assets/eip-7543/decimal_float.go +++ b/assets/eip-7543/decimal_float.go @@ -1,4 +1,4 @@ -// math based on https://github.com/JuliaMath/Decimals.jl +// arithmetic based on https://github.com/JuliaMath/Decimals.jl package vm @@ -21,7 +21,7 @@ func createDecimal(_c, _q *int256, gas *uint64) *Decimal { } // CONSTANTS -var GLOBAL_GAS uint64 +var GLOBAL_GAS uint64 // not needed, only once per node start, just as an argument into New and createDecimal var MINUS_ONE_INT256 = new(int256).Neg(ONE_INT256) var ZERO_INT256 = New(0, &GLOBAL_GAS) @@ -45,9 +45,10 @@ func (out *Decimal) Add(a, b *Decimal, precision *int256, gas *uint64) *Decimal ca := add_helper(a, b, gas) cb := add_helper(b, a, gas) - Add(&ca, &cb, &out.c, gas) - Set(min(&a.q, &b.q, gas), &out.q, gas) + + q := signedMin(&a.q, &b.q, gas) + Set(q, &out.q, gas) out.normalize(out, precision, false, gas) @@ -78,7 +79,7 @@ func (out *Decimal) Inv(a *Decimal, precision *int256, gas *uint64) *Decimal { var precision_m_aq int256 Sub(precision, &a.q, &precision_m_aq, gas) - if SignedCmp(&precision_m_aq, ZERO_INT256, gas) == -1 { + if signedCmp(&precision_m_aq, ZERO_INT256, gas) == -1 { panic("precision_m_aq NEGATIVE") } @@ -262,9 +263,9 @@ func DecSin(ac, aq, precision, steps *int256, gas *uint64) (bc, bq *int256) { // -1 if a < b // -// 0 if a == b -// 1 if b < a -func SignedCmp(a, b *int256, gas *uint64) int { +// 0 if a == b +// 1 if b < a +func signedCmp(a, b *int256, gas *uint64) int { c := a.Cmp(b) if c == 0 { // a == b @@ -297,9 +298,9 @@ func SignedCmp(a, b *int256, gas *uint64) int { } } -// min(a, b) -func min(a, b *int256, gas *uint64) (c *int256) { - if SignedCmp(a, b, gas) == -1 { +// signedMin(a, b) +func signedMin(a, b *int256, gas *uint64) (c *int256) { + if signedCmp(a, b, gas) == -1 { return a } else { return b @@ -344,7 +345,7 @@ func (a *Decimal) lessThan(b *Decimal, precision *int256, gas *uint64) bool { func signedDiv(numerator, denominator, out *int256, gas *uint64) *int256 { sn := Sign(numerator, gas) sd := Sign(denominator, gas) - if sn == 0 && sd == 0 { + if sn == 0 && sd == 0 { // TODO correct? xor just sd == 0 ? out = nil return nil } @@ -372,18 +373,18 @@ func signedDiv(numerator, denominator, out *int256, gas *uint64) *int256 { return out } -// c = (-1)^d1.s * d1.c * 10^max(d1.q - d2.q, 0) -func add_helper(d1, d2 *Decimal, gas *uint64) (c int256) { +// out = {c: d1.c, q: 10^max(d1.q - d2.q, 0)} +func add_helper(d1, d2 *Decimal, gas *uint64) (out int256) { var exponent_diff int256 Sub(&d1.q, &d2.q, &exponent_diff, gas) if Sign(&exponent_diff, gas) == -1 { exponent_diff = *ZERO_INT256 // shallow copy ok } - Exp(TEN_INT256, &exponent_diff, &c, gas) - Mul(&d1.c, &c, &c, gas) + Exp(TEN_INT256, &exponent_diff, &out, gas) + Mul(&d1.c, &out, &out, gas) - return c + return out } // remove trailing zeros from coefficient @@ -440,7 +441,7 @@ func (out *Decimal) round(a *Decimal, precision *int256, normal bool, gas *uint6 var shift, ten_power int256 Add(precision, &a.q, &shift, gas) - if SignedCmp(&shift, ZERO_INT256, gas) == 1 || SignedCmp(&shift, &a.q, gas) == -1 { + if signedCmp(&shift, ZERO_INT256, gas) == 1 || signedCmp(&shift, &a.q, gas) == -1 { if normal { Set(&a.c, &out.c, gas) Set(&a.q, &out.q, gas) From 83d94b5abb0761c459eee17245516202c7099c19 Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Fri, 3 Nov 2023 15:13:45 -0700 Subject: [PATCH 30/39] small changes --- assets/eip-7543/decimal_float_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/eip-7543/decimal_float_test.go b/assets/eip-7543/decimal_float_test.go index 510ba1ada1b084..44ae37b0c7ab4c 100644 --- a/assets/eip-7543/decimal_float_test.go +++ b/assets/eip-7543/decimal_float_test.go @@ -122,7 +122,7 @@ func TestSignedCmp(t *testing.T) { // a := uint256.NewInt(23) a := new(uint256.Int).Neg(uint256.NewInt(14)) b := new(uint256.Int).Neg(uint256.NewInt(15)) - c := SignedCmp(a, b, &gas) + c := signedCmp(a, b, &gas) fmt.Println(c) } From cee3ed270175c47cbb48b52ddb2a8167b15a9afc Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Tue, 14 Nov 2023 07:15:46 -0800 Subject: [PATCH 31/39] remove comments --- EIPS/eip-7543.md | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/EIPS/eip-7543.md b/EIPS/eip-7543.md index 446c33700b1fc9..a458f237f5b6ad 100644 --- a/EIPS/eip-7543.md +++ b/EIPS/eip-7543.md @@ -32,17 +32,6 @@ A simple value like 0.1 cannot be represented finitely in binary. Decimal types The EVM is a virtual machine and thereby not restricted by hardware. Usually, assembly languages provide OPCODES that are mimic the ability of hardware. In a virtual machine, we have no such limitations and nothing stops us from adding more complex OPCODEs, as long as fair gas is provided. At the same time, we do not want to clutter the OPCODEs library. EXP, LN and SIN are universal functions that open the path to: powers, trigonometry, integrals, differential equations, machine learning, digital art, etc. - - - ## Specification ### Decimal @@ -104,17 +93,6 @@ Using math/big would allow for arbitrary[*] intermediate precision. That version Even tho using math/big, input and output values have to fit into the stack. Using uint256 is much faster, natural for the stack and still allows for far more precision that most applications need. - - - - - The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. ## Rationale From 8c4d17245b058c64c11b8eb6021492a950aca742 Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Tue, 14 Nov 2023 07:18:22 -0800 Subject: [PATCH 32/39] capital letters beginning sentences --- EIPS/eip-7543.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/EIPS/eip-7543.md b/EIPS/eip-7543.md index a458f237f5b6ad..4cda481fb0b438 100644 --- a/EIPS/eip-7543.md +++ b/EIPS/eip-7543.md @@ -57,7 +57,7 @@ etc. 0xd5 DECLN ln(a) -> b : (ac, aq, precision, steps) -> (bc, bq) 0xd6 DECSIN sin(a) -> b : (ac, aq, precision, steps) -> (bc, bq) -precision is the # of digits kept during all calculations. steps for DECEXP and DECSIN are the # of Taylor expansion steps. steps for DECLN is the depth of the continued fractions expansion. +`precision` is the # of digits kept during all calculations. `steps` for DECEXP and DECSIN are the # of Taylor expansion steps. `steps` for DECLN is the depth of the continued fractions expansion. ### Why these functions? @@ -66,7 +66,7 @@ The proposed functions (+,-,*,/,exp,ln,sin) form a small set that combined enabl a^b = exp(b * ln(a)) gives us powers and polynomials. cos(a) = sin(tau/4-a), tan(a)=sin(a)/cos(a), etc., gives us all of trigonometry. -together with arithmetic, we get all elementary functions. +Together with arithmetic, we get all elementary functions. ### DECNEG instead of DECSUB @@ -99,19 +99,19 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S ### gas -all the above OPCODEs are deterministic, hence the gas cost can be determined. at the same time, the calculations are complex and depend on the input. +All the above OPCODEs are deterministic, hence the gas cost can be determined. At the same time, the calculations are complex and depend on the input. -it is crucial to have accurate gas costs to avoid energy attacks on nodes. +It is crucial to have accurate gas costs to avoid energy attacks on nodes. -to this end, i have wrapped the underlying uint256 lib with gas accumulation, as found in the reference implementation in ../assets/eip-EVM+/uint256_wrapped.go. this gives a bottom-up approach to calculating gas, by running the OPCODE. +To this end, i have wrapped the underlying uint256 lib with gas accumulation, as found in the reference implementation in ../assets/eip-EVM+/uint256_wrapped.go. This gives a bottom-up approach to calculating gas, by running the OPCODE. -because the EVM interprator expects the gas cost before actually running the OPCODE, we are running the OPCODE twice. the first run, identical to the second, is to get the bottom-up gas cost, which is then doubled to account for the actual run plus the gas calculation. on top, we add a fixed emulation cost. +Because the EVM interprator expects the gas cost before actually running the OPCODE, we are running the OPCODE twice. the first run, identical to the second, is to get the bottom-up gas cost, which is then doubled to account for the actual run plus the gas calculation. On top, we add a fixed emulation cost. -this gives an embedded gas calcuation, which works well for complex OPCODEs (see gasEVMPlusEmulate in ../assets/eip-EVM+/gasEVMPlusEmulate.go). +This gives an embedded gas calcuation, which works well for complex OPCODEs (see gasEVMPlusEmulate in ../assets/eip-EVM+/gasEVMPlusEmulate.go). -to remove the double gas, a future EIP would suggest the following: allow contract code to run whilst accumulating gas (at runtime) and panicking in case of limit breach, without requiring the cost in advance. this only works for contract code that is pure, defined as code that only depends on the user input and the inner bytecode of the contract. pure contracts cannot use state from the chain, nor make calls to other contracts. pure mathematical functions would e.g. be pure contracts. pure contracts are fully deterministic given the input, allowing a user to estimate gas costs offline (cheaper) and the EVM to panic at runtime, without knowing gas in advance. +To remove the double gas, a future EIP would suggest the following: allow contract code to run whilst accumulating gas (at runtime) and panicking in case of limit breach, without requiring the cost in advance. This only works for contract code that is pure, defined as code that only depends on the user input and the inner bytecode of the contract. Pure contracts cannot use state from the chain, nor make calls to other contracts. pure mathematical functions would e.g. be pure contracts. pure contracts are fully deterministic given the input, allowing a user to estimate gas costs offline (cheaper) and the EVM to panic at runtime, without knowing gas in advance. -since the costs depend on the input, a fuzzing would give us close to the worst cases (TODO). +Since the costs depend on the input, a fuzzing would give us close to the worst cases (TODO). ## Backwards Compatibility From 5084adc7f421a0221a17a439e61b18a6e0a83c93 Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Tue, 14 Nov 2023 07:19:45 -0800 Subject: [PATCH 33/39] no math/big --- EIPS/eip-7543.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/EIPS/eip-7543.md b/EIPS/eip-7543.md index 4cda481fb0b438..809fbbe1158a83 100644 --- a/EIPS/eip-7543.md +++ b/EIPS/eip-7543.md @@ -84,14 +84,6 @@ The Taylor series of exp and sin converge everywhere and fast. The error falls a Ln converges fast using continued fractions within the interval ]0,2]. The implementation scales the input into this interval and scales the result back correctly. -### math/big - -The implementation allows arbitrary precision, in theory. In practice, resources are always finite. - -The implementation uses the same lib as used for the stack (uint256). -Using math/big would allow for arbitrary[*] intermediate precision. That version is also functional, on another branch. -Even tho using math/big, input and output values have to fit into the stack. -Using uint256 is much faster, natural for the stack and still allows for far more precision that most applications need. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. From 3188e5f99b7d18bea0ccab31fc975edaed4fe7b0 Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Tue, 14 Nov 2023 07:20:59 -0800 Subject: [PATCH 34/39] grammer --- EIPS/eip-7543.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7543.md b/EIPS/eip-7543.md index 809fbbe1158a83..9ca75843d11372 100644 --- a/EIPS/eip-7543.md +++ b/EIPS/eip-7543.md @@ -95,7 +95,7 @@ All the above OPCODEs are deterministic, hence the gas cost can be determined. A It is crucial to have accurate gas costs to avoid energy attacks on nodes. -To this end, i have wrapped the underlying uint256 lib with gas accumulation, as found in the reference implementation in ../assets/eip-EVM+/uint256_wrapped.go. This gives a bottom-up approach to calculating gas, by running the OPCODE. +To this end, the underlying uint256 lib can be wrapped with gas accumulation, as found in the reference implementation in ../assets/eip-EVM+/uint256_wrapped.go. This gives a bottom-up approach to calculating gas, by running the OPCODE. Because the EVM interprator expects the gas cost before actually running the OPCODE, we are running the OPCODE twice. the first run, identical to the second, is to get the bottom-up gas cost, which is then doubled to account for the actual run plus the gas calculation. On top, we add a fixed emulation cost. From b41e882c419be92214e2efe988857df5c1ebac5f Mon Sep 17 00:00:00 2001 From: 1m1 Date: Mon, 15 Jan 2024 06:27:31 -0800 Subject: [PATCH 35/39] 0.1 infinite binary rep --- EIPS/eip-7543.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7543.md b/EIPS/eip-7543.md index 9ca75843d11372..6de29eb3674602 100644 --- a/EIPS/eip-7543.md +++ b/EIPS/eip-7543.md @@ -26,7 +26,7 @@ The files [BlackScholes.yul](../assets/eip-7543/BlackScholes.yul) and [Neuron.yu ### Why decimal? -A simple value like 0.1 cannot be represented finitely in binary. Decimal types are much closer to the vast majority of numerical calculations run by humans. +To represent a simple value like 0.1 in binary requires infinite many digits and is therefore not exactly represently in a finite binary machine. Decimal types are much closer to the vast majority of numerical calculations run by humans. ### eVm From 0a8565e125e4e878a28cc6969ed486c2f936b108 Mon Sep 17 00:00:00 2001 From: 1m1 Date: Mon, 15 Jan 2024 06:33:08 -0800 Subject: [PATCH 36/39] remove commented test code --- assets/eip-7543/decimal_float_test.go | 30 ++++++--------------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/assets/eip-7543/decimal_float_test.go b/assets/eip-7543/decimal_float_test.go index 44ae37b0c7ab4c..6bca270262f37b 100644 --- a/assets/eip-7543/decimal_float_test.go +++ b/assets/eip-7543/decimal_float_test.go @@ -118,8 +118,6 @@ func benchmarkOpDec(b *testing.B, intArgs []*uint256.Int, op executionFunc) { func TestSignedCmp(t *testing.T) { var gas uint64 - // b := uint256.NewInt(15) - // a := uint256.NewInt(23) a := new(uint256.Int).Neg(uint256.NewInt(14)) b := new(uint256.Int).Neg(uint256.NewInt(15)) c := signedCmp(a, b, &gas) @@ -143,8 +141,6 @@ func TestDecAdd(t *testing.T) { var out Decimal gas = 0 out.Add(&tt.a, &tt.b, PRECISION, &gas) - fmt.Println(gas) - // fmt.Println("a", showDecimal(&tt.a), "b", showDecimal(&tt.b), "out", showDecimal(&out), "c", showDecimal(&tt.c)) if !out.eq(&tt.c, PRECISION, &gas) { t.Fatal(tt.a, tt.b, out, tt.c) @@ -166,10 +162,6 @@ func TestDecNeg(t *testing.T) { var out Decimal gas = 0 out.Neg(&tt.a, &gas) - fmt.Println(gas) - // fmt.Println("a", showDecimal(&tt.a)) - // fmt.Println("b", showDecimal(&tt.b)) - // fmt.Println("out", showDecimal(&out)) if !out.eq(&tt.b, PRECISION, &gas) { t.Fatal(tt.a, tt.b, out) @@ -194,7 +186,7 @@ func TestDecMul(t *testing.T) { var out Decimal gas = 0 out.Mul(&tt.a, &tt.b, PRECISION, &gas) - fmt.Println(gas) + if !out.eq(&tt.c, PRECISION, &gas) { t.Fatal(tt.a, tt.b, out, tt.c) } @@ -218,8 +210,6 @@ func TestDecInv(t *testing.T) { var out Decimal gas = 0 out.Inv(&tt.a, PRECISION, &gas) - fmt.Println(gas) - // fmt.Println("a", showDecimal(&tt.a), "out", showDecimal(&out), "b", showDecimal(&tt.b)) if !out.eq(&tt.b, PRECISION, &gas) { t.Fatal(tt.a, out, tt.b) @@ -238,9 +228,7 @@ func TestDecNormalize(t *testing.T) { NEG_45 := new(uint256.Int).Neg(uint256.NewInt(45)) NEG_55 := new(uint256.Int).Neg(uint256.NewInt(55)) - // NEG_77 := new(uint256.Int).Neg(uint256.NewInt(77)) NEG_75 := new(uint256.Int).Neg(uint256.NewInt(75)) - // NEG_76 := new(uint256.Int).Neg(uint256.NewInt(76)) var TEN_48, FIVE_48, MINUS_FIVE_48 uint256.Int TEN_48.Exp(uint256.NewInt(10), uint256.NewInt(48)) @@ -265,8 +253,6 @@ func TestDecNormalize(t *testing.T) { var out Decimal gas = 0 out.normalize(&tt.a, PRECISION, tt.rounded, &gas) - fmt.Println(gas) - // fmt.Println("normalize", tt.a.String(), out.String(), tt.b.String()) if !out.eq(&tt.b, PRECISION, &gas) { t.Fatal(tt.a, out, tt.b) @@ -290,8 +276,6 @@ func TestDecExp(t *testing.T) { var out Decimal gas = 0 out.Exp(&tt.a, PRECISION, &tt.steps, &gas) - fmt.Println(gas) - // fmt.Println(out.String()) if !out.eq(&tt.b, PRECISION, &gas) { t.Fatal(tt.a, out, tt.b) @@ -315,11 +299,10 @@ func TestDecLn(t *testing.T) { var out Decimal gas = 0 out.Ln(&tt.a, PRECISION, &tt.steps, &gas) - fmt.Println(gas) - // fmt.Println(out.String()) - // if !out.eq(&tt.b, PRECISION) { - // t.Fatal(tt.a, out, tt.b) - // } + + if !out.eq(&tt.b, PRECISION) { + t.Fatal(tt.a, out, tt.b) + } } } @@ -337,8 +320,7 @@ func TestDecSin(t *testing.T) { var out Decimal gas = 0 out.Sin(&tt.a, PRECISION, &tt.steps, &gas) - fmt.Println(gas) - // fmt.Println(out.String()) + if !out.eq(&tt.b, PRECISION, &gas) { t.Fatal(tt.a, out, tt.b) } From a18c4cf0e4d4ffde6736aad6ddec905512146ed4 Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Mon, 26 Feb 2024 14:54:09 -0700 Subject: [PATCH 37/39] upper bound for gas --- EIPS/eip-7543.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/EIPS/eip-7543.md b/EIPS/eip-7543.md index 6de29eb3674602..c51a0b27795f23 100644 --- a/EIPS/eip-7543.md +++ b/EIPS/eip-7543.md @@ -101,7 +101,8 @@ Because the EVM interprator expects the gas cost before actually running the OPC This gives an embedded gas calcuation, which works well for complex OPCODEs (see gasEVMPlusEmulate in ../assets/eip-EVM+/gasEVMPlusEmulate.go). -To remove the double gas, a future EIP would suggest the following: allow contract code to run whilst accumulating gas (at runtime) and panicking in case of limit breach, without requiring the cost in advance. This only works for contract code that is pure, defined as code that only depends on the user input and the inner bytecode of the contract. Pure contracts cannot use state from the chain, nor make calls to other contracts. pure mathematical functions would e.g. be pure contracts. pure contracts are fully deterministic given the input, allowing a user to estimate gas costs offline (cheaper) and the EVM to panic at runtime, without knowing gas in advance. +To remove the double gas, we could find an upper bound on gas usage dependent on the function inputs. +Alternatively, a future EIP would suggest the following: allow contract code to run whilst accumulating gas (at runtime) and panicking in case of limit breach, without requiring the cost in advance. This only works for contract code that is pure, defined as code that only depends on the user input and the inner bytecode of the contract. Pure contracts cannot use state from the chain, nor make calls to other contracts. pure mathematical functions would e.g. be pure contracts. pure contracts are fully deterministic given the input, allowing a user to estimate gas costs offline (cheaper) and the EVM to panic at runtime, without knowing gas in advance. Since the costs depend on the input, a fuzzing would give us close to the worst cases (TODO). From 340142399880ba7d066f1d844c7847ddcfbdf238 Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Fri, 1 Mar 2024 18:01:19 -0700 Subject: [PATCH 38/39] typo, clarification --- EIPS/eip-7543.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7543.md b/EIPS/eip-7543.md index c51a0b27795f23..a8e1f585a5828a 100644 --- a/EIPS/eip-7543.md +++ b/EIPS/eip-7543.md @@ -110,7 +110,7 @@ Since the costs depend on the input, a fuzzing would give us close to the worst No backward compatibility issues found. -Since the EVM always contracts to deploy with invalid code, there could be previously invalid code that becomes valid when adding a new OPCODE. The EVM could be designed to expect a version tag at the beginning of all bytecode or (not xor) to only deploy valid code. +Since the EVM allows contracts to deploy with invalid code, there could be previously invalid code that becomes valid when adding a new OPCODE. The EVM could be designed to expect a version tag at the beginning of all bytecode or (not xor) to only deploy valid code. This is an issue for any new OPCODE. ## Test Cases From 36c27feac1447520bbb5a1694dca1b5bca47161b Mon Sep 17 00:00:00 2001 From: 1m1 <77983409+1m1-github@users.noreply.github.com> Date: Fri, 1 Mar 2024 18:06:24 -0700 Subject: [PATCH 39/39] better comment --- assets/eip-7543/Neuron.yul | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/eip-7543/Neuron.yul b/assets/eip-7543/Neuron.yul index b2ea6afd47a6ea..60cae5df293f9c 100644 --- a/assets/eip-7543/Neuron.yul +++ b/assets/eip-7543/Neuron.yul @@ -157,13 +157,13 @@ object "Neuron" { cc, cq := dec_exp(cc, cq, precision, steps) } - // dec_sqrt(a) = a^(1/2) + // dec_sqrt(a) = a^(1/2) = a^(5*10(-1)) function dec_sqrt(ac, aq, precision, steps) -> bc, bq { let MINUS_ONE := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // -1 bc, bq := pow(ac, aq, 5, MINUS_ONE, precision, steps) } - // dec_sqr(a) = a^2 + // dec_sqr(a) = a*a function dec_sqr(ac, aq, precision) -> bc, bq { bc, bq := dec_mul(ac, aq, ac, aq, precision) }