Merge pull request #1 from 0glabs/precompiles-refactor

feat: precompiles refactor
This commit is contained in:
MiniFrenchBread 2025-04-16 15:42:20 +08:00 committed by GitHub
commit 906ff6ec77
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 7934 additions and 121 deletions

3
.gitignore vendored
View file

@ -44,6 +44,9 @@ profile.cov
tests/spec-tests/
# Precompiles
core/vm/Precompiles/interfaces/node_modules
core/vm/Precompiles/interfaces/build
# binaries
cmd/abidump/abidump
cmd/abigen/abigen

View file

@ -0,0 +1,166 @@
package bn254util
import (
"math/big"
"github.com/consensys/gnark-crypto/ecc/bn254"
"github.com/consensys/gnark-crypto/ecc/bn254/fp"
"github.com/consensys/gnark-crypto/ecc/bn254/fr"
"github.com/ethereum/go-ethereum/crypto"
)
const (
G1PointSize = 32 * 2
G2PointSize = 32 * 2 * 2
)
var (
FR_MODULUS, _ = new(big.Int).SetString("21888242871839275222246405745257275088548364400416034343698204186575808495617", 10)
)
func VerifySig(sig *bn254.G1Affine, pubkey *bn254.G2Affine, msgBytes [32]byte) (bool, error) {
g2Gen := GetG2Generator()
msgPoint := MapToCurve(msgBytes)
var negSig bn254.G1Affine
negSig.Neg((*bn254.G1Affine)(sig))
P := [2]bn254.G1Affine{*msgPoint, negSig}
Q := [2]bn254.G2Affine{*pubkey, *g2Gen}
ok, err := bn254.PairingCheck(P[:], Q[:])
if err != nil {
return false, nil
}
return ok, nil
}
func MapToCurve(digest [32]byte) *bn254.G1Affine {
one := new(big.Int).SetUint64(1)
three := new(big.Int).SetUint64(3)
x := new(big.Int)
x.SetBytes(digest[:])
for {
// y = x^3 + 3
xP3 := new(big.Int).Exp(x, big.NewInt(3), fp.Modulus())
y := new(big.Int).Add(xP3, three)
y.Mod(y, fp.Modulus())
if y.ModSqrt(y, fp.Modulus()) == nil {
x.Add(x, one).Mod(x, fp.Modulus())
} else {
var fpX, fpY fp.Element
fpX.SetBigInt(x)
fpY.SetBigInt(y)
return &bn254.G1Affine{
X: fpX,
Y: fpY,
}
}
}
}
func CheckG1AndG2DiscreteLogEquality(pointG1 *bn254.G1Affine, pointG2 *bn254.G2Affine) (bool, error) {
negGenG1 := new(bn254.G1Affine).Neg(GetG1Generator())
return bn254.PairingCheck([]bn254.G1Affine{*pointG1, *negGenG1}, []bn254.G2Affine{*GetG2Generator(), *pointG2})
}
func GetG1Generator() *bn254.G1Affine {
g1Gen := new(bn254.G1Affine)
_, err := g1Gen.X.SetString("1")
if err != nil {
return nil
}
_, err = g1Gen.Y.SetString("2")
if err != nil {
return nil
}
return g1Gen
}
func GetG2Generator() *bn254.G2Affine {
g2Gen := new(bn254.G2Affine)
g2Gen.X.SetString("10857046999023057135944570762232829481370756359578518086990519993285655852781",
"11559732032986387107991004021392285783925812861821192530917403151452391805634")
g2Gen.Y.SetString("8495653923123431417604973247489272438418190587263600148770280649306958101930",
"4082367875863433681332203403145435568316851327593401208105741076214120093531")
return g2Gen
}
func MulByGeneratorG1(a *fr.Element) *bn254.G1Affine {
g1Gen := GetG1Generator()
return new(bn254.G1Affine).ScalarMultiplication(g1Gen, a.BigInt(new(big.Int)))
}
func MulByGeneratorG2(a *fr.Element) *bn254.G2Affine {
g2Gen := GetG2Generator()
return new(bn254.G2Affine).ScalarMultiplication(g2Gen, a.BigInt(new(big.Int)))
}
func SerializeG1(p *bn254.G1Affine) []byte {
b := make([]byte, 0)
tmp := p.X.Bytes()
for i := 0; i < 32; i++ {
b = append(b, tmp[i])
}
tmp = p.Y.Bytes()
for i := 0; i < 32; i++ {
b = append(b, tmp[i])
}
return b
}
func DeserializeG1(b []byte) *bn254.G1Affine {
p := new(bn254.G1Affine)
p.X.SetBytes(b[0:32])
p.Y.SetBytes(b[32:64])
return p
}
func SerializeG2(p *bn254.G2Affine) []byte {
b := make([]byte, 0)
tmp := p.X.A0.Bytes()
for i := 0; i < 32; i++ {
b = append(b, tmp[i])
}
tmp = p.X.A1.Bytes()
for i := 0; i < 32; i++ {
b = append(b, tmp[i])
}
tmp = p.Y.A0.Bytes()
for i := 0; i < 32; i++ {
b = append(b, tmp[i])
}
tmp = p.Y.A1.Bytes()
for i := 0; i < 32; i++ {
b = append(b, tmp[i])
}
return b
}
func DeserializeG2(b []byte) *bn254.G2Affine {
p := new(bn254.G2Affine)
p.X.A0.SetBytes(b[0:32])
p.X.A1.SetBytes(b[32:64])
p.Y.A0.SetBytes(b[64:96])
p.Y.A1.SetBytes(b[96:128])
return p
}
func Gamma(hash *bn254.G1Affine, signature *bn254.G1Affine, pkG1 *bn254.G1Affine, pkG2 *bn254.G2Affine) *big.Int {
toHash := make([]byte, 0)
toHash = append(toHash, SerializeG1(hash)...)
toHash = append(toHash, SerializeG1(signature)...)
toHash = append(toHash, SerializeG1(pkG1)...)
toHash = append(toHash, SerializeG2(pkG2)...)
msgHash := crypto.Keccak256(toHash)
gamma := new(big.Int)
gamma.SetBytes(msgHash)
gamma.Mod(gamma, FR_MODULUS)
return gamma
}

View file

@ -19,6 +19,7 @@ package common
import (
"bytes"
"database/sql/driver"
"encoding/binary"
"encoding/hex"
"encoding/json"
"errors"
@ -486,3 +487,9 @@ func (b PrettyBytes) TerminalString() string {
}
return fmt.Sprintf("%#x...%x (%dB)", b[:3], b[len(b)-3:], len(b))
}
func Uint64ToBytes(i uint64) []byte {
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, i)
return b
}

View file

@ -270,6 +270,9 @@ const (
// BalanceChangeRevert is emitted when the balance is reverted back to a previous value due to call failure.
// It is only emitted when the tracer has opted in to use the journaling wrapper (WrapWithJournal).
BalanceChangeRevert BalanceChangeReason = 15
// BalanceIncreaseWA0GIMint is emitted when mint happened in wrapped a0gi base precompile
BalanceIncreaseWA0GIMint BalanceChangeReason = 16
)
// GasChangeReason is used to indicate the reason for a gas change, useful

View file

@ -17,6 +17,7 @@
package vm
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"errors"
@ -29,13 +30,16 @@ import (
bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
"github.com/consensys/gnark-crypto/ecc/bls12-381/fp"
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/blake2b"
"github.com/ethereum/go-ethereum/crypto/bn256"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
"golang.org/x/crypto/ripemd160"
)
@ -43,99 +47,160 @@ import (
// requires a deterministic gas count based on the input size of the Run method of the
// contract.
type PrecompiledContract interface {
RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
Run(input []byte) ([]byte, error) // Run runs the precompiled contract
Address() common.Address
RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error)
}
// PrecompiledContracts contains the precompiled contracts supported at the given fork.
type PrecompiledContracts map[common.Address]PrecompiledContract
var CustomPrecompiledContracts = PrecompiledContracts{
(&DASignersPrecompile{}).Address(): NewDASignersPrecompile(),
(&WrappedA0giBasePrecompile{}).Address(): NewWrappedA0giBasePrecompile(),
}
// WithCustomPrecompiles merge given precompiles with custom precompiles
func WithCustomPrecompiles(base PrecompiledContracts) PrecompiledContracts {
result := make(PrecompiledContracts)
maps.Copy(result, base)
for k, v := range CustomPrecompiledContracts {
if _, exists := result[k]; !exists {
result[k] = v
} else {
panic("Precompile address collision")
}
}
return result
}
// PrecompiledContract is the basic interface for custom precompile contracts.
type StatefulPrecompiledContract interface {
PrecompiledContract
Abi() *abi.ABI
IsTx(string) bool
}
func InitializeStatefulPrecompileCall(
p StatefulPrecompiledContract,
evm *EVM,
contract *Contract,
readonly bool,
) (
method *abi.Method,
args []interface{},
err error,
) {
// parse input
if len(contract.Input) < 4 {
return nil, nil, ErrExecutionReverted
}
method, err = p.Abi().MethodById(contract.Input[:4])
if err != nil {
return nil, nil, ErrExecutionReverted
}
args, err = method.Inputs.Unpack(contract.Input[4:])
if err != nil {
return nil, nil, ErrExecutionReverted
}
// readonly check
if readonly && p.IsTx(method.Name) {
return nil, nil, ErrExecutionReverted
}
// Set code to a non-empty value to prevent it from being cleared by stateDB during commit() or finalise()
if !readonly && bytes.Equal(crypto.Keccak256Hash(evm.StateDB.GetCode(p.Address())).Bytes(), types.EmptyCodeHash.Bytes()) {
evm.StateDB.SetCode(p.Address(), []byte{1})
}
return method, args, nil
}
// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum
// contracts used in the Frontier and Homestead releases.
var PrecompiledContractsHomestead = PrecompiledContracts{
common.BytesToAddress([]byte{0x1}): &ecrecover{},
common.BytesToAddress([]byte{0x2}): &sha256hash{},
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
common.BytesToAddress([]byte{0x4}): &dataCopy{},
}
var PrecompiledContractsHomestead = WithCustomPrecompiles(PrecompiledContracts{
(&ecrecover{}).Address(): &ecrecover{},
(&sha256hash{}).Address(): &sha256hash{},
(&ripemd160hash{}).Address(): &ripemd160hash{},
(&dataCopy{}).Address(): &dataCopy{},
})
// PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum
// contracts used in the Byzantium release.
var PrecompiledContractsByzantium = PrecompiledContracts{
common.BytesToAddress([]byte{0x1}): &ecrecover{},
common.BytesToAddress([]byte{0x2}): &sha256hash{},
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
common.BytesToAddress([]byte{0x4}): &dataCopy{},
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: false},
common.BytesToAddress([]byte{0x6}): &bn256AddByzantium{},
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulByzantium{},
common.BytesToAddress([]byte{0x8}): &bn256PairingByzantium{},
}
var PrecompiledContractsByzantium = WithCustomPrecompiles(PrecompiledContracts{
(&ecrecover{}).Address(): &ecrecover{},
(&sha256hash{}).Address(): &sha256hash{},
(&ripemd160hash{}).Address(): &ripemd160hash{},
(&dataCopy{}).Address(): &dataCopy{},
(&bigModExp{eip2565: false}).Address(): &bigModExp{eip2565: false},
(&bn256AddByzantium{}).Address(): &bn256AddByzantium{},
(&bn256ScalarMulByzantium{}).Address(): &bn256ScalarMulByzantium{},
(&bn256PairingByzantium{}).Address(): &bn256PairingByzantium{},
})
// PrecompiledContractsIstanbul contains the default set of pre-compiled Ethereum
// contracts used in the Istanbul release.
var PrecompiledContractsIstanbul = PrecompiledContracts{
common.BytesToAddress([]byte{0x1}): &ecrecover{},
common.BytesToAddress([]byte{0x2}): &sha256hash{},
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
common.BytesToAddress([]byte{0x4}): &dataCopy{},
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: false},
common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{0x9}): &blake2F{},
}
var PrecompiledContractsIstanbul = WithCustomPrecompiles(PrecompiledContracts{
(&ecrecover{}).Address(): &ecrecover{},
(&sha256hash{}).Address(): &sha256hash{},
(&ripemd160hash{}).Address(): &ripemd160hash{},
(&dataCopy{}).Address(): &dataCopy{},
(&bigModExp{eip2565: false}).Address(): &bigModExp{eip2565: false},
(&bn256AddIstanbul{}).Address(): &bn256AddIstanbul{},
(&bn256ScalarMulIstanbul{}).Address(): &bn256ScalarMulIstanbul{},
(&bn256PairingIstanbul{}).Address(): &bn256PairingIstanbul{},
(&blake2F{}).Address(): &blake2F{},
})
// PrecompiledContractsBerlin contains the default set of pre-compiled Ethereum
// contracts used in the Berlin release.
var PrecompiledContractsBerlin = PrecompiledContracts{
common.BytesToAddress([]byte{0x1}): &ecrecover{},
common.BytesToAddress([]byte{0x2}): &sha256hash{},
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
common.BytesToAddress([]byte{0x4}): &dataCopy{},
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{0x9}): &blake2F{},
}
var PrecompiledContractsBerlin = WithCustomPrecompiles(PrecompiledContracts{
(&ecrecover{}).Address(): &ecrecover{},
(&sha256hash{}).Address(): &sha256hash{},
(&ripemd160hash{}).Address(): &ripemd160hash{},
(&dataCopy{}).Address(): &dataCopy{},
(&bigModExp{eip2565: true}).Address(): &bigModExp{eip2565: true},
(&bn256AddIstanbul{}).Address(): &bn256AddIstanbul{},
(&bn256ScalarMulIstanbul{}).Address(): &bn256ScalarMulIstanbul{},
(&bn256PairingIstanbul{}).Address(): &bn256PairingIstanbul{},
(&blake2F{}).Address(): &blake2F{},
})
// PrecompiledContractsCancun contains the default set of pre-compiled Ethereum
// contracts used in the Cancun release.
var PrecompiledContractsCancun = PrecompiledContracts{
common.BytesToAddress([]byte{0x1}): &ecrecover{},
common.BytesToAddress([]byte{0x2}): &sha256hash{},
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
common.BytesToAddress([]byte{0x4}): &dataCopy{},
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{0x9}): &blake2F{},
common.BytesToAddress([]byte{0xa}): &kzgPointEvaluation{},
}
var PrecompiledContractsCancun = WithCustomPrecompiles(PrecompiledContracts{
(&ecrecover{}).Address(): &ecrecover{},
(&sha256hash{}).Address(): &sha256hash{},
(&ripemd160hash{}).Address(): &ripemd160hash{},
(&dataCopy{}).Address(): &dataCopy{},
(&bigModExp{eip2565: true}).Address(): &bigModExp{eip2565: true},
(&bn256AddIstanbul{}).Address(): &bn256AddIstanbul{},
(&bn256ScalarMulIstanbul{}).Address(): &bn256ScalarMulIstanbul{},
(&bn256PairingIstanbul{}).Address(): &bn256PairingIstanbul{},
(&blake2F{}).Address(): &blake2F{},
(&kzgPointEvaluation{}).Address(): &kzgPointEvaluation{},
})
// PrecompiledContractsPrague contains the set of pre-compiled Ethereum
// contracts used in the Prague release.
var PrecompiledContractsPrague = PrecompiledContracts{
common.BytesToAddress([]byte{0x01}): &ecrecover{},
common.BytesToAddress([]byte{0x02}): &sha256hash{},
common.BytesToAddress([]byte{0x03}): &ripemd160hash{},
common.BytesToAddress([]byte{0x04}): &dataCopy{},
common.BytesToAddress([]byte{0x05}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{0x06}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{0x07}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{0x08}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{0x09}): &blake2F{},
common.BytesToAddress([]byte{0x0a}): &kzgPointEvaluation{},
common.BytesToAddress([]byte{0x0b}): &bls12381G1Add{},
common.BytesToAddress([]byte{0x0c}): &bls12381G1MultiExp{},
common.BytesToAddress([]byte{0x0d}): &bls12381G2Add{},
common.BytesToAddress([]byte{0x0e}): &bls12381G2MultiExp{},
common.BytesToAddress([]byte{0x0f}): &bls12381Pairing{},
common.BytesToAddress([]byte{0x10}): &bls12381MapG1{},
common.BytesToAddress([]byte{0x11}): &bls12381MapG2{},
}
var PrecompiledContractsPrague = WithCustomPrecompiles(PrecompiledContracts{
(&ecrecover{}).Address(): &ecrecover{},
(&sha256hash{}).Address(): &sha256hash{},
(&ripemd160hash{}).Address(): &ripemd160hash{},
(&dataCopy{}).Address(): &dataCopy{},
(&bigModExp{eip2565: true}).Address(): &bigModExp{eip2565: true},
(&bn256AddIstanbul{}).Address(): &bn256AddIstanbul{},
(&bn256ScalarMulIstanbul{}).Address(): &bn256ScalarMulIstanbul{},
(&bn256PairingIstanbul{}).Address(): &bn256PairingIstanbul{},
(&blake2F{}).Address(): &blake2F{},
(&kzgPointEvaluation{}).Address(): &kzgPointEvaluation{},
(&bls12381G1Add{}).Address(): &bls12381G1Add{},
(&bls12381G1MultiExp{}).Address(): &bls12381G1MultiExp{},
(&bls12381G2Add{}).Address(): &bls12381G2Add{},
(&bls12381G2MultiExp{}).Address(): &bls12381G2MultiExp{},
(&bls12381Pairing{}).Address(): &bls12381Pairing{},
(&bls12381MapG1{}).Address(): &bls12381MapG1{},
(&bls12381MapG2{}).Address(): &bls12381MapG2{},
})
var PrecompiledContractsBLS = PrecompiledContractsPrague
@ -218,30 +283,52 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
// - the returned bytes,
// - the _remaining_ gas,
// - any error that occurred
func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) {
func RunPrecompiledContract(
evm *EVM,
p PrecompiledContract,
caller common.Address,
input []byte,
suppliedGas uint64,
value *uint256.Int,
readOnly bool,
logger *tracing.Hooks,
) (ret []byte, remainingGas uint64, err error) {
inputCopy := make([]byte, len(input))
copy(inputCopy, input)
var jumpDests map[common.Hash]bitvec
if evm != nil {
jumpDests = evm.jumpDests
}
contract := NewContract(caller, p.Address(), value, suppliedGas, jumpDests)
contract.Input = inputCopy
gasCost := p.RequiredGas(input)
if suppliedGas < gasCost {
if !contract.UseGas(gasCost, logger, tracing.GasChangeCallPrecompiledContract) {
return nil, 0, ErrOutOfGas
}
if logger != nil && logger.OnGasChange != nil {
logger.OnGasChange(suppliedGas, suppliedGas-gasCost, tracing.GasChangeCallPrecompiledContract)
}
suppliedGas -= gasCost
output, err := p.Run(input)
return output, suppliedGas, err
output, err := p.Run(evm, contract, readOnly)
return output, contract.Gas, err
}
// ecrecover implemented as a native contract.
type ecrecover struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*ecrecover) Address() common.Address {
return common.BytesToAddress([]byte{0x1})
}
func (c *ecrecover) RequiredGas(input []byte) uint64 {
return params.EcrecoverGas
}
func (c *ecrecover) Run(input []byte) ([]byte, error) {
func (c *ecrecover) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
const ecRecoverInputLength = 128
input = common.RightPadBytes(input, ecRecoverInputLength)
input := common.RightPadBytes(contract.Input, ecRecoverInputLength)
// "input" is (hash, v, r, s), each 32 bytes
// but for ecrecover we want (r, s, v)
@ -272,6 +359,12 @@ func (c *ecrecover) Run(input []byte) ([]byte, error) {
// SHA256 implemented as a native contract.
type sha256hash struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*sha256hash) Address() common.Address {
return common.BytesToAddress([]byte{0x2})
}
// RequiredGas returns the gas required to execute the pre-compiled contract.
//
// This method does not require any overflow checking as the input size gas costs
@ -279,14 +372,20 @@ type sha256hash struct{}
func (c *sha256hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas
}
func (c *sha256hash) Run(input []byte) ([]byte, error) {
h := sha256.Sum256(input)
func (c *sha256hash) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
h := sha256.Sum256(contract.Input)
return h[:], nil
}
// RIPEMD160 implemented as a native contract.
type ripemd160hash struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*ripemd160hash) Address() common.Address {
return common.BytesToAddress([]byte{0x3})
}
// RequiredGas returns the gas required to execute the pre-compiled contract.
//
// This method does not require any overflow checking as the input size gas costs
@ -294,15 +393,21 @@ type ripemd160hash struct{}
func (c *ripemd160hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas
}
func (c *ripemd160hash) Run(input []byte) ([]byte, error) {
func (c *ripemd160hash) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
ripemd := ripemd160.New()
ripemd.Write(input)
ripemd.Write(contract.Input)
return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
}
// data copy implemented as a native contract.
type dataCopy struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*dataCopy) Address() common.Address {
return common.BytesToAddress([]byte{0x4})
}
// RequiredGas returns the gas required to execute the pre-compiled contract.
//
// This method does not require any overflow checking as the input size gas costs
@ -310,8 +415,8 @@ type dataCopy struct{}
func (c *dataCopy) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas
}
func (c *dataCopy) Run(in []byte) ([]byte, error) {
return common.CopyBytes(in), nil
func (c *dataCopy) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
return common.CopyBytes(contract.Input), nil
}
// bigModExp implements a native big integer exponential modular operation.
@ -319,6 +424,12 @@ type bigModExp struct {
eip2565 bool
}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*bigModExp) Address() common.Address {
return common.BytesToAddress([]byte{0x5})
}
var (
big1 = big.NewInt(1)
big3 = big.NewInt(3)
@ -441,7 +552,8 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 {
return gas.Uint64()
}
func (c *bigModExp) Run(input []byte) ([]byte, error) {
func (c *bigModExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
input := contract.Input
var (
baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64()
expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64()
@ -516,26 +628,38 @@ func runBn256Add(input []byte) ([]byte, error) {
// Istanbul consensus rules.
type bn256AddIstanbul struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*bn256AddIstanbul) Address() common.Address {
return common.BytesToAddress([]byte{0x6})
}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 {
return params.Bn256AddGasIstanbul
}
func (c *bn256AddIstanbul) Run(input []byte) ([]byte, error) {
return runBn256Add(input)
func (c *bn256AddIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
return runBn256Add(contract.Input)
}
// bn256AddByzantium implements a native elliptic curve point addition
// conforming to Byzantium consensus rules.
type bn256AddByzantium struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*bn256AddByzantium) Address() common.Address {
return common.BytesToAddress([]byte{0x6})
}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 {
return params.Bn256AddGasByzantium
}
func (c *bn256AddByzantium) Run(input []byte) ([]byte, error) {
return runBn256Add(input)
func (c *bn256AddByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
return runBn256Add(contract.Input)
}
// runBn256ScalarMul implements the Bn256ScalarMul precompile, referenced by
@ -554,26 +678,38 @@ func runBn256ScalarMul(input []byte) ([]byte, error) {
// multiplication conforming to Istanbul consensus rules.
type bn256ScalarMulIstanbul struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*bn256ScalarMulIstanbul) Address() common.Address {
return common.BytesToAddress([]byte{0x7})
}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 {
return params.Bn256ScalarMulGasIstanbul
}
func (c *bn256ScalarMulIstanbul) Run(input []byte) ([]byte, error) {
return runBn256ScalarMul(input)
func (c *bn256ScalarMulIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
return runBn256ScalarMul(contract.Input)
}
// bn256ScalarMulByzantium implements a native elliptic curve scalar
// multiplication conforming to Byzantium consensus rules.
type bn256ScalarMulByzantium struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*bn256ScalarMulByzantium) Address() common.Address {
return common.BytesToAddress([]byte{0x7})
}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 {
return params.Bn256ScalarMulGasByzantium
}
func (c *bn256ScalarMulByzantium) Run(input []byte) ([]byte, error) {
return runBn256ScalarMul(input)
func (c *bn256ScalarMulByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
return runBn256ScalarMul(contract.Input)
}
var (
@ -622,30 +758,48 @@ func runBn256Pairing(input []byte) ([]byte, error) {
// conforming to Istanbul consensus rules.
type bn256PairingIstanbul struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*bn256PairingIstanbul) Address() common.Address {
return common.BytesToAddress([]byte{0x8})
}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 {
return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul
}
func (c *bn256PairingIstanbul) Run(input []byte) ([]byte, error) {
return runBn256Pairing(input)
func (c *bn256PairingIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
return runBn256Pairing(contract.Input)
}
// bn256PairingByzantium implements a pairing pre-compile for the bn256 curve
// conforming to Byzantium consensus rules.
type bn256PairingByzantium struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*bn256PairingByzantium) Address() common.Address {
return common.BytesToAddress([]byte{0x8})
}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 {
return params.Bn256PairingBaseGasByzantium + uint64(len(input)/192)*params.Bn256PairingPerPointGasByzantium
}
func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) {
return runBn256Pairing(input)
func (c *bn256PairingByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
return runBn256Pairing(contract.Input)
}
type blake2F struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*blake2F) Address() common.Address {
return common.BytesToAddress([]byte{0x9})
}
func (c *blake2F) RequiredGas(input []byte) uint64 {
// If the input is malformed, we can't calculate the gas, return 0 and let the
// actual call choke and fault.
@ -666,7 +820,8 @@ var (
errBlake2FInvalidFinalFlag = errors.New("invalid final flag")
)
func (c *blake2F) Run(input []byte) ([]byte, error) {
func (c *blake2F) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
input := contract.Input
// Make sure the input is valid (correct length and final flag)
if len(input) != blake2FInputLength {
return nil, errBlake2FInvalidInputLength
@ -715,12 +870,19 @@ var (
// bls12381G1Add implements EIP-2537 G1Add precompile.
type bls12381G1Add struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*bls12381G1Add) Address() common.Address {
return common.BytesToAddress([]byte{0x0b})
}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bls12381G1Add) RequiredGas(input []byte) uint64 {
return params.Bls12381G1AddGas
}
func (c *bls12381G1Add) Run(input []byte) ([]byte, error) {
func (c *bls12381G1Add) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
input := contract.Input
// Implements EIP-2537 G1Add precompile.
// > G1 addition call expects `256` bytes as an input that is interpreted as byte concatenation of two G1 points (`128` bytes each).
// > Output is an encoding of addition operation result - single G1 point (`128` bytes).
@ -751,6 +913,12 @@ func (c *bls12381G1Add) Run(input []byte) ([]byte, error) {
// bls12381G1MultiExp implements EIP-2537 G1MultiExp precompile.
type bls12381G1MultiExp struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*bls12381G1MultiExp) Address() common.Address {
return common.BytesToAddress([]byte{0x0c})
}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 {
// Calculate G1 point, scalar value pair length
@ -770,7 +938,8 @@ func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 {
return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000
}
func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) {
func (c *bls12381G1MultiExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
input := contract.Input
// Implements EIP-2537 G1MultiExp precompile.
// G1 multiplication call expects `160*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes).
// Output is an encoding of multiexponentiation operation result - single G1 point (`128` bytes).
@ -811,12 +980,19 @@ func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) {
// bls12381G2Add implements EIP-2537 G2Add precompile.
type bls12381G2Add struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*bls12381G2Add) Address() common.Address {
return common.BytesToAddress([]byte{0x0d})
}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bls12381G2Add) RequiredGas(input []byte) uint64 {
return params.Bls12381G2AddGas
}
func (c *bls12381G2Add) Run(input []byte) ([]byte, error) {
func (c *bls12381G2Add) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
input := contract.Input
// Implements EIP-2537 G2Add precompile.
// > G2 addition call expects `512` bytes as an input that is interpreted as byte concatenation of two G2 points (`256` bytes each).
// > Output is an encoding of addition operation result - single G2 point (`256` bytes).
@ -848,6 +1024,12 @@ func (c *bls12381G2Add) Run(input []byte) ([]byte, error) {
// bls12381G2MultiExp implements EIP-2537 G2MultiExp precompile.
type bls12381G2MultiExp struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*bls12381G2MultiExp) Address() common.Address {
return common.BytesToAddress([]byte{0x0e})
}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 {
// Calculate G2 point, scalar value pair length
@ -867,7 +1049,8 @@ func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 {
return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000
}
func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) {
func (c *bls12381G2MultiExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
input := contract.Input
// Implements EIP-2537 G2MultiExp precompile logic
// > G2 multiplication call expects `288*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes).
// > Output is an encoding of multiexponentiation operation result - single G2 point (`256` bytes).
@ -908,12 +1091,19 @@ func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) {
// bls12381Pairing implements EIP-2537 Pairing precompile.
type bls12381Pairing struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*bls12381Pairing) Address() common.Address {
return common.BytesToAddress([]byte{0x0f})
}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bls12381Pairing) RequiredGas(input []byte) uint64 {
return params.Bls12381PairingBaseGas + uint64(len(input)/384)*params.Bls12381PairingPerPairGas
}
func (c *bls12381Pairing) Run(input []byte) ([]byte, error) {
func (c *bls12381Pairing) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
input := contract.Input
// Implements EIP-2537 Pairing precompile logic.
// > Pairing call expects `384*k` bytes as an inputs that is interpreted as byte concatenation of `k` slices. Each slice has the following structure:
// > - `128` bytes of G1 point encoding
@ -1060,12 +1250,19 @@ func encodePointG2(p *bls12381.G2Affine) []byte {
// bls12381MapG1 implements EIP-2537 MapG1 precompile.
type bls12381MapG1 struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*bls12381MapG1) Address() common.Address {
return common.BytesToAddress([]byte{0x10})
}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bls12381MapG1) RequiredGas(input []byte) uint64 {
return params.Bls12381MapG1Gas
}
func (c *bls12381MapG1) Run(input []byte) ([]byte, error) {
func (c *bls12381MapG1) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
input := contract.Input
// Implements EIP-2537 Map_To_G1 precompile.
// > Field-to-curve call expects an `64` bytes input that is interpreted as an element of the base field.
// > Output of this call is `128` bytes and is G1 point following respective encoding rules.
@ -1089,12 +1286,19 @@ func (c *bls12381MapG1) Run(input []byte) ([]byte, error) {
// bls12381MapG2 implements EIP-2537 MapG2 precompile.
type bls12381MapG2 struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*bls12381MapG2) Address() common.Address {
return common.BytesToAddress([]byte{0x11})
}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bls12381MapG2) RequiredGas(input []byte) uint64 {
return params.Bls12381MapG2Gas
}
func (c *bls12381MapG2) Run(input []byte) ([]byte, error) {
func (c *bls12381MapG2) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
input := contract.Input
// Implements EIP-2537 Map_FP2_TO_G2 precompile logic.
// > Field-to-curve call expects an `128` bytes input that is interpreted as an element of the quadratic extension field.
// > Output of this call is `256` bytes and is G2 point following respective encoding rules.
@ -1122,6 +1326,12 @@ func (c *bls12381MapG2) Run(input []byte) ([]byte, error) {
// kzgPointEvaluation implements the EIP-4844 point evaluation precompile.
type kzgPointEvaluation struct{}
// Address defines the precompiled contract address. This MUST match the address
// set in the precompiled contract map.
func (*kzgPointEvaluation) Address() common.Address {
return common.BytesToAddress([]byte{0xa})
}
// RequiredGas estimates the gas required for running the point evaluation precompile.
func (b *kzgPointEvaluation) RequiredGas(input []byte) uint64 {
return params.BlobTxPointEvaluationPrecompileGas
@ -1140,7 +1350,8 @@ var (
)
// Run executes the point evaluation precompile.
func (b *kzgPointEvaluation) Run(input []byte) ([]byte, error) {
func (b *kzgPointEvaluation) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
input := contract.Input
if len(input) != blobVerifyInputLength {
return nil, errBlobVerifyInvalidInputLength
}

View file

@ -20,6 +20,7 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
)
func FuzzPrecompiledContracts(f *testing.F) {
@ -36,7 +37,7 @@ func FuzzPrecompiledContracts(f *testing.F) {
return
}
inWant := string(input)
RunPrecompiledContract(p, input, gas, nil)
RunPrecompiledContract(nil, p, common.Address{}, input, gas, uint256.NewInt(0), false, nil)
if inHave := string(input); inWant != inHave {
t.Errorf("Precompiled %v modified input data", a)
}

View file

@ -25,6 +25,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
)
// precompiledTest defines the input/output pairs for precompiled contract tests.
@ -96,7 +97,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
in := common.Hex2Bytes(test.Input)
gas := p.RequiredGas(in)
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
if res, _, err := RunPrecompiledContract(p, in, gas, nil); err != nil {
if res, _, err := RunPrecompiledContract(nil, p, common.Address{}, in, gas, uint256.NewInt(0), false, nil); err != nil {
t.Error(err)
} else if common.Bytes2Hex(res) != test.Expected {
t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))
@ -118,7 +119,7 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
gas := p.RequiredGas(in) - 1
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
_, _, err := RunPrecompiledContract(p, in, gas, nil)
_, _, err := RunPrecompiledContract(nil, p, common.Address{}, in, gas, uint256.NewInt(0), false, nil)
if err.Error() != "out of gas" {
t.Errorf("Expected error [out of gas], got [%v]", err)
}
@ -135,7 +136,7 @@ func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing
in := common.Hex2Bytes(test.Input)
gas := p.RequiredGas(in)
t.Run(test.Name, func(t *testing.T) {
_, _, err := RunPrecompiledContract(p, in, gas, nil)
_, _, err := RunPrecompiledContract(nil, p, common.Address{}, in, gas, uint256.NewInt(0), false, nil)
if err.Error() != test.ExpectedError {
t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err)
}
@ -167,7 +168,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
copy(data, in)
res, _, err = RunPrecompiledContract(p, data, reqGas, nil)
res, _, err = RunPrecompiledContract(nil, p, common.Address{}, data, reqGas, uint256.NewInt(0), false, nil)
}
bench.StopTimer()
elapsed := uint64(time.Since(start))

664
core/vm/dasigners.go Normal file

File diff suppressed because one or more lines are too long

366
core/vm/dasigners_test.go Normal file
View file

@ -0,0 +1,366 @@
package vm
import (
"math/big"
"testing"
"github.com/consensys/gnark-crypto/ecc/bn254"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/bn254util"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm/precompiles/dasigners"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
"github.com/stretchr/testify/suite"
)
type DASignersTestSuite struct {
suite.Suite
abi abi.ABI
statedb *state.StateDB
evm *EVM
dasigners *DASignersPrecompile
signerOne common.Address
signerTwo common.Address
}
func (suite *DASignersTestSuite) SetupTest() {
suite.dasigners = NewDASignersPrecompile()
suite.abi = suite.dasigners.abi
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
suite.statedb = statedb
suite.evm = NewEVM(BlockContext{
BlockNumber: big.NewInt(1),
}, statedb, params.TestChainConfig, Config{})
suite.signerOne = common.HexToAddress("0x0000000000000000000000000000000100000000")
suite.signerTwo = common.HexToAddress("0x0000000000000000000000000000000100000001")
}
func (suite *DASignersTestSuite) runTx(input []byte, signer common.Address, gas uint64, value *uint256.Int, readonly bool) ([]byte, error) {
suite.evm.Origin = signer
bz, _, err := RunPrecompiledContract(suite.evm, suite.dasigners, signer, input, gas, value, readonly, nil)
if err == nil {
suite.statedb.Finalise(true)
suite.statedb.Commit(suite.evm.Context.BlockNumber.Uint64(), true, false)
}
return bz, err
}
func (suite *DASignersTestSuite) registerSigner(testSigner common.Address, sk *big.Int) *dasigners.IDASignersSignerDetail {
pkG1 := new(bn254.G1Affine).ScalarMultiplication(bn254util.GetG1Generator(), sk)
pkG2 := new(bn254.G2Affine).ScalarMultiplication(bn254util.GetG2Generator(), sk)
hash := dasigners.PubkeyRegistrationHash(testSigner, suite.evm.chainConfig.ChainID)
signature := new(bn254.G1Affine).ScalarMultiplication(hash, sk)
signer := dasigners.IDASignersSignerDetail{
Signer: testSigner,
Socket: "0.0.0.0:1234",
PkG1: dasigners.NewBN254G1Point(bn254util.SerializeG1(pkG1)),
PkG2: dasigners.NewBN254G2Point(bn254util.SerializeG2(pkG2)),
}
input, err := suite.abi.Pack(
"registerSigner",
signer,
dasigners.NewBN254G1Point(bn254util.SerializeG1(signature)),
)
suite.Assert().NoError(err)
oldLogs := suite.statedb.Logs()
_, err = suite.runTx(input, suite.dasigners.getRegistry(), 10000000, uint256.NewInt(0), false)
suite.Assert().NoError(err)
logs := suite.statedb.Logs()
suite.Assert().EqualValues(len(logs), len(oldLogs)+2)
_, err = suite.abi.Unpack("SocketUpdated", logs[len(logs)-1].Data)
suite.Assert().NoError(err)
_, err = suite.abi.Unpack("NewSigner", logs[len(logs)-2].Data)
suite.Assert().NoError(err)
return &signer
}
func (suite *DASignersTestSuite) makeEpoch(testSigner common.Address) {
input, err := suite.abi.Pack(
"makeEpoch",
)
suite.Assert().NoError(err)
_, err = suite.runTx(input, testSigner, 10000000, uint256.NewInt(0), false)
suite.Assert().NoError(err)
}
func (suite *DASignersTestSuite) updateSocket(testSigner common.Address, signer *dasigners.IDASignersSignerDetail) {
input, err := suite.abi.Pack(
"updateSocket",
"0.0.0.0:2345",
)
suite.Assert().NoError(err)
oldLogs := suite.statedb.Logs()
_, err = suite.runTx(input, testSigner, 10000000, uint256.NewInt(0), false)
suite.Assert().NoError(err)
logs := suite.statedb.Logs()
suite.Assert().EqualValues(len(logs), len(oldLogs)+1)
_, err = suite.abi.Unpack("SocketUpdated", logs[len(logs)-1].Data)
suite.Assert().NoError(err)
signer.Socket = "0.0.0.0:2345"
}
func (suite *DASignersTestSuite) registerEpoch(testSigner common.Address, sk *big.Int, votes *big.Int) {
epoch := suite.dasigners.epochNumber(suite.evm) + 1
hash := dasigners.EpochRegistrationHash(testSigner, epoch, suite.evm.chainConfig.ChainID)
signature := new(bn254.G1Affine).ScalarMultiplication(hash, sk)
input, err := suite.abi.Pack(
"registerNextEpoch",
testSigner,
dasigners.NewBN254G1Point(bn254util.SerializeG1(signature)),
votes,
)
suite.Assert().NoError(err)
_, err = suite.runTx(input, suite.dasigners.getRegistry(), 10000000, uint256.NewInt(0), false)
suite.Assert().NoError(err)
}
func (suite *DASignersTestSuite) queryEpochNumber(testSigner common.Address, expected *big.Int) {
input, err := suite.abi.Pack(
"epochNumber",
)
suite.Assert().NoError(err)
bz, err := suite.runTx(input, testSigner, 10000000, uint256.NewInt(0), true)
suite.Assert().NoError(err)
out, err := suite.abi.Methods["epochNumber"].Outputs.Unpack(bz)
suite.Assert().NoError(err)
suite.Assert().EqualValues(out[0].(*big.Int).Uint64(), expected.Uint64())
}
func (suite *DASignersTestSuite) queryQuorumCount(testSigner common.Address) {
epoch := suite.dasigners.epochNumber(suite.evm)
input, err := suite.abi.Pack(
"quorumCount",
big.NewInt(int64(epoch)),
)
suite.Assert().NoError(err)
bz, err := suite.runTx(input, testSigner, 10000000, uint256.NewInt(0), true)
suite.Assert().NoError(err)
out, err := suite.abi.Methods["quorumCount"].Outputs.Unpack(bz)
suite.Assert().NoError(err)
suite.Assert().EqualValues(out[0], big.NewInt(1))
}
func (suite *DASignersTestSuite) queryGetSigner(testSigner common.Address, answer []dasigners.IDASignersSignerDetail) {
input, err := suite.abi.Pack(
"getSigner",
[]common.Address{suite.signerOne, suite.signerTwo},
)
suite.Assert().NoError(err)
bz, err := suite.runTx(input, testSigner, 10000000, uint256.NewInt(0), true)
suite.Assert().NoError(err)
out, err := suite.abi.Methods["getSigner"].Outputs.Unpack(bz)
suite.Assert().NoError(err)
suite.Assert().EqualValues(out[0], answer)
}
func (suite *DASignersTestSuite) queryIsSigner(testSigner common.Address) {
input, err := suite.abi.Pack(
"isSigner",
suite.signerOne,
)
suite.Assert().NoError(err)
bz, err := suite.runTx(input, testSigner, 10000000, uint256.NewInt(0), true)
suite.Assert().NoError(err)
out, err := suite.abi.Methods["isSigner"].Outputs.Unpack(bz)
suite.Assert().NoError(err)
suite.Assert().EqualValues(out[0], true)
}
func (suite *DASignersTestSuite) queryRegisteredEpoch(testSigner common.Address, account common.Address, epoch *big.Int) bool {
input, err := suite.abi.Pack(
"registeredEpoch",
account,
epoch,
)
suite.Assert().NoError(err)
bz, err := suite.runTx(input, testSigner, 10000000, uint256.NewInt(0), true)
suite.Assert().NoError(err)
out, err := suite.abi.Methods["registeredEpoch"].Outputs.Unpack(bz)
suite.Assert().NoError(err)
return out[0].(bool)
}
func (suite *DASignersTestSuite) queryGetQuorum(testSigner common.Address) []common.Address {
epoch := suite.dasigners.epochNumber(suite.evm)
input, err := suite.abi.Pack(
"getQuorum",
big.NewInt(int64(epoch)),
big.NewInt(0),
)
suite.Assert().NoError(err)
bz, err := suite.runTx(input, testSigner, 10000000, uint256.NewInt(0), true)
suite.Assert().NoError(err)
out, err := suite.abi.Methods["getQuorum"].Outputs.Unpack(bz)
suite.Assert().NoError(err)
return out[0].([]common.Address)
}
func (suite *DASignersTestSuite) queryGetQuorumRow(testSigner common.Address, row uint32) common.Address {
epoch := suite.dasigners.epochNumber(suite.evm)
input, err := suite.abi.Pack(
"getQuorumRow",
big.NewInt(int64(epoch)),
big.NewInt(0),
row,
)
suite.Assert().NoError(err)
bz, err := suite.runTx(input, testSigner, 10000000, uint256.NewInt(0), true)
suite.Assert().NoError(err)
out, err := suite.abi.Methods["getQuorumRow"].Outputs.Unpack(bz)
suite.Assert().NoError(err)
return out[0].(common.Address)
}
func (suite *DASignersTestSuite) queryGetAggPkG1(testSigner common.Address, bitmap []byte) struct {
AggPkG1 dasigners.BN254G1Point
Total *big.Int
Hit *big.Int
} {
epoch := suite.dasigners.epochNumber(suite.evm)
input, err := suite.abi.Pack(
"getAggPkG1",
big.NewInt(int64(epoch)),
big.NewInt(0),
bitmap,
)
suite.Assert().NoError(err)
bz, err := suite.runTx(input, testSigner, 10000000, uint256.NewInt(0), true)
suite.Assert().NoError(err)
out, err := suite.abi.Methods["getAggPkG1"].Outputs.Unpack(bz)
suite.Assert().NoError(err)
return struct {
AggPkG1 dasigners.BN254G1Point
Total *big.Int
Hit *big.Int
}{
AggPkG1: out[0].(dasigners.BN254G1Point),
Total: out[1].(*big.Int),
Hit: out[2].(*big.Int),
}
}
func (suite *DASignersTestSuite) Test_DASigners() {
// tx test
signer1 := suite.registerSigner(suite.signerOne, big.NewInt(1))
signer2 := suite.registerSigner(suite.signerTwo, big.NewInt(11))
suite.updateSocket(suite.signerOne, signer1)
suite.updateSocket(suite.signerTwo, signer2)
suite.registerEpoch(suite.signerOne, big.NewInt(1), big.NewInt(1))
suite.registerEpoch(suite.signerTwo, big.NewInt(11), big.NewInt(2))
// move to next epochs
daparams := suite.dasigners.params()
suite.queryEpochNumber(suite.signerOne, big.NewInt(0))
// move to epoch 1 & register
suite.makeEpoch(suite.signerOne)
suite.queryEpochNumber(suite.signerOne, big.NewInt(1))
suite.makeEpoch(suite.signerOne)
suite.queryEpochNumber(suite.signerOne, big.NewInt(1))
suite.registerEpoch(suite.signerOne, big.NewInt(1), big.NewInt(1))
suite.registerEpoch(suite.signerTwo, big.NewInt(11), big.NewInt(2))
// move to epoch 2
suite.evm.Context.BlockNumber = suite.evm.Context.BlockNumber.Add(suite.evm.Context.BlockNumber, daparams.EpochBlocks)
suite.makeEpoch(suite.signerOne)
suite.queryEpochNumber(suite.signerOne, big.NewInt(2))
// query test
suite.queryQuorumCount(suite.signerOne)
suite.queryGetSigner(suite.signerOne, []dasigners.IDASignersSignerDetail{*signer1, *signer2})
suite.queryIsSigner(suite.signerOne)
suite.Assert().EqualValues(suite.queryRegisteredEpoch(suite.signerOne, suite.signerOne, big.NewInt(0)), false)
suite.Assert().EqualValues(suite.queryRegisteredEpoch(suite.signerOne, suite.signerTwo, big.NewInt(0)), false)
suite.Assert().EqualValues(suite.queryRegisteredEpoch(suite.signerOne, suite.signerOne, big.NewInt(1)), true)
suite.Assert().EqualValues(suite.queryRegisteredEpoch(suite.signerOne, suite.signerTwo, big.NewInt(1)), true)
suite.Assert().EqualValues(suite.queryRegisteredEpoch(suite.signerOne, suite.signerOne, big.NewInt(2)), true)
suite.Assert().EqualValues(suite.queryRegisteredEpoch(suite.signerOne, suite.signerTwo, big.NewInt(2)), true)
suite.Assert().EqualValues(suite.queryRegisteredEpoch(suite.signerOne, suite.signerOne, big.NewInt(3)), false)
suite.Assert().EqualValues(suite.queryRegisteredEpoch(suite.signerOne, suite.signerTwo, big.NewInt(3)), false)
quorum := suite.queryGetQuorum(suite.signerOne)
suite.Assert().EqualValues(len(quorum), daparams.EncodedSlices.Int64())
cnt := map[common.Address]int{suite.signerOne: 0, suite.signerTwo: 0}
onePos := len(quorum)
twoPos := len(quorum)
for i, v := range quorum {
suite.Assert().EqualValues(suite.queryGetQuorumRow(suite.signerOne, uint32(i)), v)
cnt[v] += 1
if v == suite.signerOne {
onePos = min(onePos, i)
} else {
twoPos = min(twoPos, i)
}
}
suite.Assert().EqualValues(cnt[suite.signerOne], len(quorum)/3)
suite.Assert().EqualValues(cnt[suite.signerTwo], len(quorum)*2/3)
bitMap := make([]byte, len(quorum)/8)
bitMap[onePos/8] |= 1 << (onePos % 8)
suite.Assert().EqualValues(suite.queryGetAggPkG1(suite.signerOne, bitMap), struct {
AggPkG1 dasigners.BN254G1Point
Total *big.Int
Hit *big.Int
}{
AggPkG1: dasigners.NewBN254G1Point(bn254util.SerializeG1(new(bn254.G1Affine).ScalarMultiplication(bn254util.GetG1Generator(), big.NewInt(1)))),
Total: big.NewInt(int64(len(quorum))),
Hit: big.NewInt(int64(len(quorum) / 3)),
})
bitMap[twoPos/8] |= 1 << (twoPos % 8)
suite.Assert().EqualValues(suite.queryGetAggPkG1(suite.signerOne, bitMap), struct {
AggPkG1 dasigners.BN254G1Point
Total *big.Int
Hit *big.Int
}{
AggPkG1: dasigners.NewBN254G1Point(bn254util.SerializeG1(new(bn254.G1Affine).ScalarMultiplication(bn254util.GetG1Generator(), big.NewInt(1+11)))),
Total: big.NewInt(int64(len(quorum))),
Hit: big.NewInt(int64(len(quorum))),
})
}
func (suite *DASignersTestSuite) getParams() dasigners.IDASignersParams {
input, err := suite.abi.Pack(
"params",
)
suite.Assert().NoError(err)
bz, err := suite.runTx(input, suite.signerOne, 10000000, uint256.NewInt(0), false)
suite.Assert().NoError(err)
out, err := suite.abi.Methods["params"].Outputs.Unpack(bz)
suite.Assert().NoError(err)
params := out[0].(dasigners.IDASignersParams)
return params
}
func (suite *DASignersTestSuite) Test_Params() {
daParams := suite.getParams()
expected := suite.dasigners.params()
suite.Assert().EqualValues(expected.TokensPerVote, daParams.TokensPerVote)
suite.Assert().EqualValues(expected.MaxVotesPerSigner, daParams.MaxVotesPerSigner)
suite.Assert().EqualValues(expected.MaxQuorums, daParams.MaxQuorums)
suite.Assert().EqualValues(expected.EpochBlocks, daParams.EpochBlocks)
suite.Assert().EqualValues(expected.EncodedSlices, daParams.EncodedSlices)
}
func TestKeeperSuite(t *testing.T) {
suite.Run(t, new(DASignersTestSuite))
}

View file

@ -224,7 +224,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
evm.Context.Transfer(evm.StateDB, caller, addr, value)
if isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
ret, gas, err = RunPrecompiledContract(evm, p, caller, input, gas, value, false, evm.Config.Tracer)
} else {
// Initialise a new contract and set the code that is to be used by the EVM.
code := evm.resolveCode(addr)
@ -288,7 +288,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
// It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
ret, gas, err = RunPrecompiledContract(evm, p, caller, input, gas, value, true, evm.Config.Tracer)
} else {
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
@ -331,7 +331,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
// It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
ret, gas, err = RunPrecompiledContract(evm, p, caller, input, gas, value, true, evm.Config.Tracer)
} else {
// Initialise a new contract and make initialise the delegate values
//
@ -383,7 +383,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
evm.StateDB.AddBalance(addr, new(uint256.Int), tracing.BalanceChangeTouchAccount)
if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
ret, gas, err = RunPrecompiledContract(evm, p, caller, input, gas, new(uint256.Int), true, evm.Config.Tracer)
} else {
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,14 @@
package dasigners
import "errors"
var (
ErrQuorumIdOutOfBound = errors.New("quorum id out of bound")
ErrEpochOutOfBound = errors.New("epoch out of bound")
ErrRowIdOfBound = errors.New("row id out of bound")
ErrQuorumBitmapLengthMismatch = errors.New("quorum bitmap length mismatch")
ErrSignerNotFound = errors.New("signer not found")
ErrInvalidSender = errors.New("invalid sender")
ErrSignerExists = errors.New("signer exists")
ErrInvalidSignature = errors.New("invalid signature")
)

View file

@ -0,0 +1,63 @@
package dasigners
import (
"math/big"
"github.com/consensys/gnark-crypto/ecc/bn254"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/bn254util"
"github.com/ethereum/go-ethereum/crypto"
)
func PubkeyRegistrationHash(operatorAddress common.Address, chainId *big.Int) *bn254.G1Affine {
toHash := make([]byte, 0)
toHash = append(toHash, operatorAddress.Bytes()...)
// make sure chainId is 32 bytes
toHash = append(toHash, common.LeftPadBytes(chainId.Bytes(), 32)...)
toHash = append(toHash, []byte("0G_BN254_Pubkey_Registration")...)
msgHash := crypto.Keccak256(toHash)
// convert to [32]byte
var msgHash32 [32]byte
copy(msgHash32[:], msgHash)
// hash to G1
return bn254util.MapToCurve(msgHash32)
}
func EpochRegistrationHash(operatorAddress common.Address, epoch uint64, chainId *big.Int) *bn254.G1Affine {
toHash := make([]byte, 0)
toHash = append(toHash, operatorAddress.Bytes()...)
toHash = append(toHash, common.Uint64ToBytes(epoch)...)
toHash = append(toHash, common.LeftPadBytes(chainId.Bytes(), 32)...)
msgHash := crypto.Keccak256(toHash)
// convert to [32]byte
var msgHash32 [32]byte
copy(msgHash32[:], msgHash)
// hash to G1
return bn254util.MapToCurve(msgHash32)
}
func ValidateSignature(s IDASignersSignerDetail, hash *bn254.G1Affine, signature *bn254.G1Affine) bool {
pubkeyG1 := bn254util.DeserializeG1(SerializeG1(s.PkG1))
pubkeyG2 := bn254util.DeserializeG2(SerializeG2(s.PkG2))
gamma := bn254util.Gamma(hash, signature, pubkeyG1, pubkeyG2)
// pairing
P := [2]bn254.G1Affine{
*new(bn254.G1Affine).Add(signature, new(bn254.G1Affine).ScalarMultiplication(pubkeyG1, gamma)),
*new(bn254.G1Affine).Add(hash, new(bn254.G1Affine).ScalarMultiplication(bn254util.GetG1Generator(), gamma)),
}
Q := [2]bn254.G2Affine{
*new(bn254.G2Affine).Neg(bn254util.GetG2Generator()),
*pubkeyG2,
}
ok, err := bn254.PairingCheck(P[:], Q[:])
if err != nil {
return false
}
return ok
}

View file

@ -0,0 +1,117 @@
package dasigners
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
type BN254G1Point = struct {
X *big.Int "json:\"X\""
Y *big.Int "json:\"Y\""
}
type BN254G2Point = struct {
X [2]*big.Int "json:\"X\""
Y [2]*big.Int "json:\"Y\""
}
type IDASignersSignerDetail = struct {
Signer common.Address "json:\"signer\""
Socket string "json:\"socket\""
PkG1 BN254G1Point "json:\"pkG1\""
PkG2 BN254G2Point "json:\"pkG2\""
}
type IDASignersParams = struct {
TokensPerVote *big.Int "json:\"tokensPerVote\""
MaxVotesPerSigner *big.Int "json:\"maxVotesPerSigner\""
MaxQuorums *big.Int "json:\"maxQuorums\""
EpochBlocks *big.Int "json:\"epochBlocks\""
EncodedSlices *big.Int "json:\"encodedSlices\""
}
func NewBN254G1Point(b []byte) BN254G1Point {
return BN254G1Point{
X: new(big.Int).SetBytes(b[:32]),
Y: new(big.Int).SetBytes(b[32:64]),
}
}
func SerializeG1(p BN254G1Point) []byte {
b := make([]byte, 0)
b = append(b, common.LeftPadBytes(p.X.Bytes(), 32)...)
b = append(b, common.LeftPadBytes(p.Y.Bytes(), 32)...)
return b
}
func NewBN254G2Point(b []byte) BN254G2Point {
return BN254G2Point{
X: [2]*big.Int{
new(big.Int).SetBytes(b[:32]),
new(big.Int).SetBytes(b[32:64]),
},
Y: [2]*big.Int{
new(big.Int).SetBytes(b[64:96]),
new(big.Int).SetBytes(b[96:128]),
},
}
}
func SerializeG2(p BN254G2Point) []byte {
b := make([]byte, 0)
b = append(b, common.LeftPadBytes(p.X[0].Bytes(), 32)...)
b = append(b, common.LeftPadBytes(p.X[1].Bytes(), 32)...)
b = append(b, common.LeftPadBytes(p.Y[0].Bytes(), 32)...)
b = append(b, common.LeftPadBytes(p.Y[1].Bytes(), 32)...)
return b
}
var (
signerKey = []byte{0x00} // signer => registered signer info
quorumKey = []byte{0x01} // epoch => quorumId => quorum
registrationKey = []byte{0x02} // epoch => signer => signature(vrf)
votesKey = []byte{0x03} // epoch => signer => votes
quorumCountKey = []byte{0x04} // epoch => quorum count
epochNumberKey = []byte{0x05} // epoch number
epochBlockKey = []byte{0x06} // epoch number => block number
epochRegistrationKey = []byte{0x07} // epoch number => registration count
epochRegisteredSignerKey = []byte{0x08} // epoch number => index => signer
)
func SignerKey(account common.Address) common.Hash {
return crypto.Keccak256Hash(append(signerKey, account.Bytes()...))
}
func QuorumKey(epochNumber uint64, quorumId uint64) common.Hash {
return crypto.Keccak256Hash(append(append(quorumKey, common.Uint64ToBytes(epochNumber)...), common.Uint64ToBytes(quorumId)...))
}
func RegistrationKey(epochNumber uint64, account common.Address) common.Hash {
return crypto.Keccak256Hash(append(append(registrationKey, common.Uint64ToBytes(epochNumber)...), account.Bytes()...))
}
func VotesKey(epochNumber uint64, account common.Address) common.Hash {
return crypto.Keccak256Hash(append(append(votesKey, common.Uint64ToBytes(epochNumber)...), account.Bytes()...))
}
func QuorumCountKey(epochNumber uint64) common.Hash {
return crypto.Keccak256Hash(append(quorumCountKey, common.Uint64ToBytes(epochNumber)...))
}
func EpochNumberKey() common.Hash {
return crypto.Keccak256Hash(epochNumberKey)
}
func EpochBlockKey(epochNumber uint64) common.Hash {
return crypto.Keccak256Hash(append(epochBlockKey, common.Uint64ToBytes(epochNumber)...))
}
func EpochRegistrationKey(epochNumber uint64) common.Hash {
return crypto.Keccak256Hash(append(epochRegistrationKey, common.Uint64ToBytes(epochNumber)...))
}
func EpochRegisteredSignerKey(epochNumber uint64, index uint64) common.Hash {
return crypto.Keccak256Hash(append(append(epochRegisteredSignerKey, common.Uint64ToBytes(epochNumber)...), common.Uint64ToBytes(index)...))
}

View file

@ -0,0 +1,8 @@
package precompiles
import "errors"
var (
ErrSenderNotOrigin = errors.New("sender not origin")
ErrSenderNotRegistry = errors.New("sender not registry")
)

View file

@ -0,0 +1,18 @@
{
"extends": "solhint:recommended",
"plugins": ["prettier"],
"rules": {
"avoid-low-level-calls": "off",
"compiler-version": "off",
"gas-custom-errors": "off",
"explicit-types": ["warn", "implicit"],
"func-visibility": ["warn", { "ignoreConstructors": true }],
"max-states-count": "off",
"no-empty-blocks": "off",
"no-global-import": "off",
"no-inline-assembly": "off",
"not-rely-on-time": "off",
"prettier/prettier": "error",
"reason-string": "off"
}
}

View file

@ -0,0 +1,485 @@
[
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "signer",
"type": "address"
},
{
"components": [
{
"internalType": "uint256",
"name": "X",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "Y",
"type": "uint256"
}
],
"indexed": false,
"internalType": "struct BN254.G1Point",
"name": "pkG1",
"type": "tuple"
},
{
"components": [
{
"internalType": "uint256[2]",
"name": "X",
"type": "uint256[2]"
},
{
"internalType": "uint256[2]",
"name": "Y",
"type": "uint256[2]"
}
],
"indexed": false,
"internalType": "struct BN254.G2Point",
"name": "pkG2",
"type": "tuple"
}
],
"name": "NewSigner",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "signer",
"type": "address"
},
{
"indexed": false,
"internalType": "string",
"name": "socket",
"type": "string"
}
],
"name": "SocketUpdated",
"type": "event"
},
{
"inputs": [],
"name": "epochNumber",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_epoch",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_quorumId",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "_quorumBitmap",
"type": "bytes"
}
],
"name": "getAggPkG1",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "X",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "Y",
"type": "uint256"
}
],
"internalType": "struct BN254.G1Point",
"name": "aggPkG1",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "total",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "hit",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_epoch",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_quorumId",
"type": "uint256"
}
],
"name": "getQuorum",
"outputs": [
{
"internalType": "address[]",
"name": "",
"type": "address[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_epoch",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_quorumId",
"type": "uint256"
},
{
"internalType": "uint32",
"name": "_rowIndex",
"type": "uint32"
}
],
"name": "getQuorumRow",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address[]",
"name": "_account",
"type": "address[]"
}
],
"name": "getSigner",
"outputs": [
{
"components": [
{
"internalType": "address",
"name": "signer",
"type": "address"
},
{
"internalType": "string",
"name": "socket",
"type": "string"
},
{
"components": [
{
"internalType": "uint256",
"name": "X",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "Y",
"type": "uint256"
}
],
"internalType": "struct BN254.G1Point",
"name": "pkG1",
"type": "tuple"
},
{
"components": [
{
"internalType": "uint256[2]",
"name": "X",
"type": "uint256[2]"
},
{
"internalType": "uint256[2]",
"name": "Y",
"type": "uint256[2]"
}
],
"internalType": "struct BN254.G2Point",
"name": "pkG2",
"type": "tuple"
}
],
"internalType": "struct IDASigners.SignerDetail[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_account",
"type": "address"
}
],
"name": "isSigner",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "makeEpoch",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "params",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "tokensPerVote",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "maxVotesPerSigner",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "maxQuorums",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "epochBlocks",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "encodedSlices",
"type": "uint256"
}
],
"internalType": "struct IDASigners.Params",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_epoch",
"type": "uint256"
}
],
"name": "quorumCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "signer",
"type": "address"
},
{
"components": [
{
"internalType": "uint256",
"name": "X",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "Y",
"type": "uint256"
}
],
"internalType": "struct BN254.G1Point",
"name": "_signature",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "votes",
"type": "uint256"
}
],
"name": "registerNextEpoch",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "signer",
"type": "address"
},
{
"internalType": "string",
"name": "socket",
"type": "string"
},
{
"components": [
{
"internalType": "uint256",
"name": "X",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "Y",
"type": "uint256"
}
],
"internalType": "struct BN254.G1Point",
"name": "pkG1",
"type": "tuple"
},
{
"components": [
{
"internalType": "uint256[2]",
"name": "X",
"type": "uint256[2]"
},
{
"internalType": "uint256[2]",
"name": "Y",
"type": "uint256[2]"
}
],
"internalType": "struct BN254.G2Point",
"name": "pkG2",
"type": "tuple"
}
],
"internalType": "struct IDASigners.SignerDetail",
"name": "_signer",
"type": "tuple"
},
{
"components": [
{
"internalType": "uint256",
"name": "X",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "Y",
"type": "uint256"
}
],
"internalType": "struct BN254.G1Point",
"name": "_signature",
"type": "tuple"
}
],
"name": "registerSigner",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_account",
"type": "address"
},
{
"internalType": "uint256",
"name": "_epoch",
"type": "uint256"
}
],
"name": "registeredEpoch",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "_socket",
"type": "string"
}
],
"name": "updateSocket",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]

View file

@ -0,0 +1,110 @@
[
{
"inputs": [
{
"internalType": "address",
"name": "minter",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "burn",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "getWA0GI",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "minter",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "mint",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "minter",
"type": "address"
}
],
"name": "minterSupply",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "cap",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "initialSupply",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "supply",
"type": "uint256"
}
],
"internalType": "struct Supply",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "minter",
"type": "address"
},
{
"internalType": "uint256",
"name": "cap",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "initialSupply",
"type": "uint256"
}
],
"name": "setMinterCap",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]

View file

@ -0,0 +1,94 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.8.0;
library BN254 {
struct G1Point {
uint X;
uint Y;
}
// Encoding of field elements is: X[1] * i + X[0]
struct G2Point {
uint[2] X;
uint[2] Y;
}
}
interface IDASigners {
/*=== struct ===*/
struct SignerDetail {
address signer;
string socket;
BN254.G1Point pkG1;
BN254.G2Point pkG2;
}
struct Params {
uint tokensPerVote; // deprecated
uint maxVotesPerSigner;
uint maxQuorums;
uint epochBlocks;
uint encodedSlices;
}
/*=== event ===*/
event NewSigner(
address indexed signer,
BN254.G1Point pkG1,
BN254.G2Point pkG2
);
event SocketUpdated(address indexed signer, string socket);
/*=== function ===*/
function params() external view returns (Params memory);
function epochNumber() external view returns (uint);
function quorumCount(uint _epoch) external view returns (uint);
function isSigner(address _account) external view returns (bool);
function getSigner(
address[] memory _account
) external view returns (SignerDetail[] memory);
function getQuorum(
uint _epoch,
uint _quorumId
) external view returns (address[] memory);
function getQuorumRow(
uint _epoch,
uint _quorumId,
uint32 _rowIndex
) external view returns (address);
function registerSigner(
SignerDetail memory _signer,
BN254.G1Point memory _signature
) external;
function updateSocket(string memory _socket) external;
function registeredEpoch(
address _account,
uint _epoch
) external view returns (bool);
function registerNextEpoch(
address signer,
BN254.G1Point memory _signature,
uint votes
) external;
function makeEpoch() external;
function getAggPkG1(
uint _epoch,
uint _quorumId,
bytes memory _quorumBitmap
)
external
view
returns (BN254.G1Point memory aggPkG1, uint total, uint hit);
}

View file

@ -0,0 +1,70 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.8.0;
struct Supply {
uint256 cap;
uint256 initialSupply;
uint256 supply;
}
/**
* @title WrappedA0GIBase is a precompile for wrapped a0gi(wA0GI), it enables wA0GI mint/burn native 0g token directly.
*/
interface IWrappedA0GIBase {
/**
* @dev set the wA0GI address.
* It is designed to be called by governance module only so it's not implemented at EVM precompile side.
* @param addr address of wA0GI
*/
// function setWA0GI(address addr) external;
/**
* @dev get the wA0GI address.
*/
function getWA0GI() external view returns (address);
/**
* @dev set the cap and initial supply for a minter.
* It is designed to be called by governance module only so it's not implemented at EVM precompile side.
* @param minter minter address
* @param cap mint cap
* @param initialSupply initial mint supply
*/
// function setMinterCap(address minter, uint256 cap, uint256 initialSupply) external;
/**
* @dev get the mint supply of given address
* @param minter minter address
*/
function minterSupply(address minter) external view returns (Supply memory);
/**
* @dev mint a0gi to this precompile, add corresponding amount to minter's mint supply.
* If sender's final mint supply exceeds its mint cap, the transaction will revert.
* Can only be called by WA0GI.
* @param minter minter address
* @param amount amount to mint
*/
function mint(address minter, uint256 amount) external;
/**
* @dev burn given amount of a0gi on behalf of minter, reduce corresponding amount from sender's mint supply.
* Can only be called by WA0GI.
* @param minter minter address
* @param amount amount to burn
*/
function burn(address minter, uint256 amount) external;
/**
* @dev Modify the mint and burn cap of given account.
* Can only be called by multi-sig admin.
* @param minter minter address
* @param cap new mint cap
* @param initialSupply new initial supply / burn cap
*/
function setMinterCap(
address minter,
uint256 cap,
uint256 initialSupply
) external;
}

View file

@ -0,0 +1,33 @@
import "hardhat-abi-exporter";
import { HardhatUserConfig } from "hardhat/types";
const config: HardhatUserConfig = {
paths: {
artifacts: "build/artifacts",
cache: "build/cache",
sources: "contracts",
},
solidity: {
compilers: [
{
version: "0.8.20",
settings: {
evmVersion: "istanbul",
optimizer: {
enabled: true,
runs: 200,
},
},
},
],
},
abiExporter: {
path: "./abis",
runOnCompile: true,
clear: true,
flat: true,
format: "json",
},
};
export default config;

View file

@ -0,0 +1,27 @@
{
"name": "precompile-contracts",
"version": "1.0.0",
"license": "MIT",
"scripts": {
"build": "hardhat compile",
"fmt:sol": "prettier 'contracts/**/*.sol' -w"
},
"devDependencies": {
"@nomicfoundation/hardhat-ethers": "^3.0.5",
"@typescript-eslint/eslint-plugin": "6.21.0",
"@typescript-eslint/parser": "6.21.0",
"eslint": "8.34.0",
"eslint-config-prettier": "8.6.0",
"eslint-plugin-no-only-tests": "3.1.0",
"eslint-plugin-prettier": "4.2.1",
"hardhat": "^2.22.2",
"hardhat-abi-exporter": "^2.10.1",
"prettier": "2.8.4",
"prettier-plugin-organize-imports": "3.2.4",
"prettier-plugin-solidity": "1.1.2",
"solhint": "^4.5.4",
"solhint-plugin-prettier": "0.0.5",
"ts-node": "^10.9.2",
"typescript": "4.9.5"
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,305 @@
// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
package wrappeda0gibase
import (
"errors"
"math/big"
"strings"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
)
// Reference imports to suppress errors if they are not otherwise used.
var (
_ = errors.New
_ = big.NewInt
_ = strings.NewReader
_ = ethereum.NotFound
_ = bind.Bind
_ = common.Big1
_ = types.BloomLookup
_ = event.NewSubscription
)
// Wrappeda0gibaseMetaData contains all meta data concerning the Wrappeda0gibase contract.
var Wrappeda0gibaseMetaData = &bind.MetaData{
ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getWA0GI\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"}],\"name\":\"minterSupply\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"cap\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"initialSupply\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"supply\",\"type\":\"uint256\"}],\"internalType\":\"structSupply\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cap\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"initialSupply\",\"type\":\"uint256\"}],\"name\":\"setMinterCap\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
}
// Wrappeda0gibaseABI is the input ABI used to generate the binding from.
// Deprecated: Use Wrappeda0gibaseMetaData.ABI instead.
var Wrappeda0gibaseABI = Wrappeda0gibaseMetaData.ABI
// Wrappeda0gibase is an auto generated Go binding around an Ethereum contract.
type Wrappeda0gibase struct {
Wrappeda0gibaseCaller // Read-only binding to the contract
Wrappeda0gibaseTransactor // Write-only binding to the contract
Wrappeda0gibaseFilterer // Log filterer for contract events
}
// Wrappeda0gibaseCaller is an auto generated read-only Go binding around an Ethereum contract.
type Wrappeda0gibaseCaller struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// Wrappeda0gibaseTransactor is an auto generated write-only Go binding around an Ethereum contract.
type Wrappeda0gibaseTransactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// Wrappeda0gibaseFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
type Wrappeda0gibaseFilterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// Wrappeda0gibaseSession is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options.
type Wrappeda0gibaseSession struct {
Contract *Wrappeda0gibase // Generic contract binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// Wrappeda0gibaseCallerSession is an auto generated read-only Go binding around an Ethereum contract,
// with pre-set call options.
type Wrappeda0gibaseCallerSession struct {
Contract *Wrappeda0gibaseCaller // Generic contract caller binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
}
// Wrappeda0gibaseTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
// with pre-set transact options.
type Wrappeda0gibaseTransactorSession struct {
Contract *Wrappeda0gibaseTransactor // Generic contract transactor binding to set the session for
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// Wrappeda0gibaseRaw is an auto generated low-level Go binding around an Ethereum contract.
type Wrappeda0gibaseRaw struct {
Contract *Wrappeda0gibase // Generic contract binding to access the raw methods on
}
// Wrappeda0gibaseCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
type Wrappeda0gibaseCallerRaw struct {
Contract *Wrappeda0gibaseCaller // Generic read-only contract binding to access the raw methods on
}
// Wrappeda0gibaseTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
type Wrappeda0gibaseTransactorRaw struct {
Contract *Wrappeda0gibaseTransactor // Generic write-only contract binding to access the raw methods on
}
// NewWrappeda0gibase creates a new instance of Wrappeda0gibase, bound to a specific deployed contract.
func NewWrappeda0gibase(address common.Address, backend bind.ContractBackend) (*Wrappeda0gibase, error) {
contract, err := bindWrappeda0gibase(address, backend, backend, backend)
if err != nil {
return nil, err
}
return &Wrappeda0gibase{Wrappeda0gibaseCaller: Wrappeda0gibaseCaller{contract: contract}, Wrappeda0gibaseTransactor: Wrappeda0gibaseTransactor{contract: contract}, Wrappeda0gibaseFilterer: Wrappeda0gibaseFilterer{contract: contract}}, nil
}
// NewWrappeda0gibaseCaller creates a new read-only instance of Wrappeda0gibase, bound to a specific deployed contract.
func NewWrappeda0gibaseCaller(address common.Address, caller bind.ContractCaller) (*Wrappeda0gibaseCaller, error) {
contract, err := bindWrappeda0gibase(address, caller, nil, nil)
if err != nil {
return nil, err
}
return &Wrappeda0gibaseCaller{contract: contract}, nil
}
// NewWrappeda0gibaseTransactor creates a new write-only instance of Wrappeda0gibase, bound to a specific deployed contract.
func NewWrappeda0gibaseTransactor(address common.Address, transactor bind.ContractTransactor) (*Wrappeda0gibaseTransactor, error) {
contract, err := bindWrappeda0gibase(address, nil, transactor, nil)
if err != nil {
return nil, err
}
return &Wrappeda0gibaseTransactor{contract: contract}, nil
}
// NewWrappeda0gibaseFilterer creates a new log filterer instance of Wrappeda0gibase, bound to a specific deployed contract.
func NewWrappeda0gibaseFilterer(address common.Address, filterer bind.ContractFilterer) (*Wrappeda0gibaseFilterer, error) {
contract, err := bindWrappeda0gibase(address, nil, nil, filterer)
if err != nil {
return nil, err
}
return &Wrappeda0gibaseFilterer{contract: contract}, nil
}
// bindWrappeda0gibase binds a generic wrapper to an already deployed contract.
func bindWrappeda0gibase(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := abi.JSON(strings.NewReader(Wrappeda0gibaseABI))
if err != nil {
return nil, err
}
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_Wrappeda0gibase *Wrappeda0gibaseRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _Wrappeda0gibase.Contract.Wrappeda0gibaseCaller.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_Wrappeda0gibase *Wrappeda0gibaseRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _Wrappeda0gibase.Contract.Wrappeda0gibaseTransactor.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_Wrappeda0gibase *Wrappeda0gibaseRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _Wrappeda0gibase.Contract.Wrappeda0gibaseTransactor.contract.Transact(opts, method, params...)
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_Wrappeda0gibase *Wrappeda0gibaseCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _Wrappeda0gibase.Contract.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_Wrappeda0gibase *Wrappeda0gibaseTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _Wrappeda0gibase.Contract.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_Wrappeda0gibase *Wrappeda0gibaseTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _Wrappeda0gibase.Contract.contract.Transact(opts, method, params...)
}
// GetWA0GI is a free data retrieval call binding the contract method 0xa9283a7a.
//
// Solidity: function getWA0GI() view returns(address)
func (_Wrappeda0gibase *Wrappeda0gibaseCaller) GetWA0GI(opts *bind.CallOpts) (common.Address, error) {
var out []interface{}
err := _Wrappeda0gibase.contract.Call(opts, &out, "getWA0GI")
if err != nil {
return *new(common.Address), err
}
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
return out0, err
}
// GetWA0GI is a free data retrieval call binding the contract method 0xa9283a7a.
//
// Solidity: function getWA0GI() view returns(address)
func (_Wrappeda0gibase *Wrappeda0gibaseSession) GetWA0GI() (common.Address, error) {
return _Wrappeda0gibase.Contract.GetWA0GI(&_Wrappeda0gibase.CallOpts)
}
// GetWA0GI is a free data retrieval call binding the contract method 0xa9283a7a.
//
// Solidity: function getWA0GI() view returns(address)
func (_Wrappeda0gibase *Wrappeda0gibaseCallerSession) GetWA0GI() (common.Address, error) {
return _Wrappeda0gibase.Contract.GetWA0GI(&_Wrappeda0gibase.CallOpts)
}
// MinterSupply is a free data retrieval call binding the contract method 0x95609212.
//
// Solidity: function minterSupply(address minter) view returns((uint256,uint256,uint256))
func (_Wrappeda0gibase *Wrappeda0gibaseCaller) MinterSupply(opts *bind.CallOpts, minter common.Address) (Supply, error) {
var out []interface{}
err := _Wrappeda0gibase.contract.Call(opts, &out, "minterSupply", minter)
if err != nil {
return *new(Supply), err
}
out0 := *abi.ConvertType(out[0], new(Supply)).(*Supply)
return out0, err
}
// MinterSupply is a free data retrieval call binding the contract method 0x95609212.
//
// Solidity: function minterSupply(address minter) view returns((uint256,uint256,uint256))
func (_Wrappeda0gibase *Wrappeda0gibaseSession) MinterSupply(minter common.Address) (Supply, error) {
return _Wrappeda0gibase.Contract.MinterSupply(&_Wrappeda0gibase.CallOpts, minter)
}
// MinterSupply is a free data retrieval call binding the contract method 0x95609212.
//
// Solidity: function minterSupply(address minter) view returns((uint256,uint256,uint256))
func (_Wrappeda0gibase *Wrappeda0gibaseCallerSession) MinterSupply(minter common.Address) (Supply, error) {
return _Wrappeda0gibase.Contract.MinterSupply(&_Wrappeda0gibase.CallOpts, minter)
}
// Burn is a paid mutator transaction binding the contract method 0x9dc29fac.
//
// Solidity: function burn(address minter, uint256 amount) returns()
func (_Wrappeda0gibase *Wrappeda0gibaseTransactor) Burn(opts *bind.TransactOpts, minter common.Address, amount *big.Int) (*types.Transaction, error) {
return _Wrappeda0gibase.contract.Transact(opts, "burn", minter, amount)
}
// Burn is a paid mutator transaction binding the contract method 0x9dc29fac.
//
// Solidity: function burn(address minter, uint256 amount) returns()
func (_Wrappeda0gibase *Wrappeda0gibaseSession) Burn(minter common.Address, amount *big.Int) (*types.Transaction, error) {
return _Wrappeda0gibase.Contract.Burn(&_Wrappeda0gibase.TransactOpts, minter, amount)
}
// Burn is a paid mutator transaction binding the contract method 0x9dc29fac.
//
// Solidity: function burn(address minter, uint256 amount) returns()
func (_Wrappeda0gibase *Wrappeda0gibaseTransactorSession) Burn(minter common.Address, amount *big.Int) (*types.Transaction, error) {
return _Wrappeda0gibase.Contract.Burn(&_Wrappeda0gibase.TransactOpts, minter, amount)
}
// Mint is a paid mutator transaction binding the contract method 0x40c10f19.
//
// Solidity: function mint(address minter, uint256 amount) returns()
func (_Wrappeda0gibase *Wrappeda0gibaseTransactor) Mint(opts *bind.TransactOpts, minter common.Address, amount *big.Int) (*types.Transaction, error) {
return _Wrappeda0gibase.contract.Transact(opts, "mint", minter, amount)
}
// Mint is a paid mutator transaction binding the contract method 0x40c10f19.
//
// Solidity: function mint(address minter, uint256 amount) returns()
func (_Wrappeda0gibase *Wrappeda0gibaseSession) Mint(minter common.Address, amount *big.Int) (*types.Transaction, error) {
return _Wrappeda0gibase.Contract.Mint(&_Wrappeda0gibase.TransactOpts, minter, amount)
}
// Mint is a paid mutator transaction binding the contract method 0x40c10f19.
//
// Solidity: function mint(address minter, uint256 amount) returns()
func (_Wrappeda0gibase *Wrappeda0gibaseTransactorSession) Mint(minter common.Address, amount *big.Int) (*types.Transaction, error) {
return _Wrappeda0gibase.Contract.Mint(&_Wrappeda0gibase.TransactOpts, minter, amount)
}
// SetMinterCap is a paid mutator transaction binding the contract method 0xdddba6c8.
//
// Solidity: function setMinterCap(address minter, uint256 cap, uint256 initialSupply) returns()
func (_Wrappeda0gibase *Wrappeda0gibaseTransactor) SetMinterCap(opts *bind.TransactOpts, minter common.Address, cap *big.Int, initialSupply *big.Int) (*types.Transaction, error) {
return _Wrappeda0gibase.contract.Transact(opts, "setMinterCap", minter, cap, initialSupply)
}
// SetMinterCap is a paid mutator transaction binding the contract method 0xdddba6c8.
//
// Solidity: function setMinterCap(address minter, uint256 cap, uint256 initialSupply) returns()
func (_Wrappeda0gibase *Wrappeda0gibaseSession) SetMinterCap(minter common.Address, cap *big.Int, initialSupply *big.Int) (*types.Transaction, error) {
return _Wrappeda0gibase.Contract.SetMinterCap(&_Wrappeda0gibase.TransactOpts, minter, cap, initialSupply)
}
// SetMinterCap is a paid mutator transaction binding the contract method 0xdddba6c8.
//
// Solidity: function setMinterCap(address minter, uint256 cap, uint256 initialSupply) returns()
func (_Wrappeda0gibase *Wrappeda0gibaseTransactorSession) SetMinterCap(minter common.Address, cap *big.Int, initialSupply *big.Int) (*types.Transaction, error) {
return _Wrappeda0gibase.Contract.SetMinterCap(&_Wrappeda0gibase.TransactOpts, minter, cap, initialSupply)
}

View file

@ -0,0 +1,10 @@
package wrappeda0gibase
import "errors"
var (
ErrSenderNotWA0GI = errors.New("sender is not WA0GI")
ErrSenderNotAgency = errors.New("sender is not agency")
ErrInsufficientMintCap = errors.New("insufficient mint cap")
ErrInsufficientMintSupply = errors.New("insufficient mint supply")
)

View file

@ -0,0 +1,22 @@
package wrappeda0gibase
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
type Supply = struct {
Cap *big.Int "json:\"cap\""
InitialSupply *big.Int "json:\"initialSupply\""
Supply *big.Int "json:\"supply\""
}
var (
supplyKey = []byte{0x00}
)
func SupplyKey(account common.Address) common.Hash {
return crypto.Keccak256Hash(append(supplyKey, account.Bytes()...))
}

46
core/vm/statedb_utils.go Normal file
View file

@ -0,0 +1,46 @@
package vm
import (
"bytes"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
// StoreBytes stores an arbitrary-length []byte into StateDB using multiple storage slots.
func StoreBytes(statedb StateDB, addr common.Address, key common.Hash, value []byte) {
// Compute the storage root key (keccak256(key)) as the base
lengthKey := crypto.Keccak256Hash(key.Bytes())
statedb.SetState(addr, lengthKey, common.BigToHash(big.NewInt(int64(len(value)))))
// Split value into 32-byte chunks and store them
for i := 0; i < len(value); i += 32 {
end := i + 32
if end > len(value) {
end = len(value)
}
chunk := make([]byte, 32)
copy(chunk, value[i:end]) // Copy to ensure 32 bytes, padding with zeros
// Compute storage key: keccak256(lengthKey || index)
storageKey := crypto.Keccak256Hash(append(lengthKey.Bytes(), common.Uint64ToBytes(uint64(i/32))...))
statedb.SetState(addr, storageKey, common.BytesToHash(chunk))
}
}
// LoadBytes retrieves an arbitrary-length []byte from StateDB.
func LoadBytes(statedb StateDB, addr common.Address, key common.Hash) []byte {
// Read length
lengthKey := crypto.Keccak256Hash(key.Bytes())
length := statedb.GetState(addr, lengthKey).Big().Uint64()
// Read stored chunks
var buffer bytes.Buffer
for i := uint64(0); i < length; i += 32 {
storageKey := crypto.Keccak256Hash(append(lengthKey.Bytes(), common.Uint64ToBytes(uint64(i/32))...))
chunk := statedb.GetState(addr, storageKey).Bytes()
buffer.Write(chunk)
}
return buffer.Bytes()[:length] // Trim padding
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,657 @@
package vm
import (
"fmt"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
wrappeda0gibase "github.com/ethereum/go-ethereum/core/vm/precompiles/wrapped_a0gi_base"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
"github.com/stretchr/testify/suite"
)
type WrappedA0giBaseTestSuite struct {
suite.Suite
abi abi.ABI
statedb *state.StateDB
evm *EVM
wa0gibase *WrappedA0giBasePrecompile
signerOne common.Address
signerTwo common.Address
}
func (suite *WrappedA0giBaseTestSuite) SetupTest() {
suite.wa0gibase = NewWrappedA0giBasePrecompile()
suite.abi = suite.wa0gibase.abi
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
suite.statedb = statedb
suite.evm = NewEVM(BlockContext{
BlockNumber: big.NewInt(1),
}, statedb, params.TestChainConfig, Config{})
suite.signerOne = common.HexToAddress("0x0000000000000000000000000000000100000000")
suite.signerTwo = common.HexToAddress("0x0000000000000000000000000000000100000001")
}
func (suite *WrappedA0giBaseTestSuite) runTx(input []byte, signer common.Address, gas uint64, value *uint256.Int, readonly bool) ([]byte, error) {
suite.evm.Origin = signer
bz, _, err := RunPrecompiledContract(suite.evm, suite.wa0gibase, signer, input, gas, value, readonly, nil)
if err == nil {
suite.statedb.Finalise(true)
suite.statedb.Commit(suite.evm.Context.BlockNumber.Uint64(), true, false)
}
return bz, err
}
func (s *WrappedA0giBaseTestSuite) TestGetW0GI() {
method := WrappedA0GIBaseFunctionGetWA0GI
testCases := []struct {
name string
malleate func() []byte
postCheck func(bz []byte)
gas uint64
expErr bool
errContains string
}{
{
"success",
func() []byte {
input, err := s.abi.Pack(
method,
)
s.Assert().NoError(err)
return input
},
func(data []byte) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
wa0gi := out[0].(common.Address)
s.Require().Equal(wa0gi, s.wa0gibase.getWA0GI())
// fmt.Println(wa0gi)
},
100000,
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
bz, err := s.runTx(tc.malleate(), s.signerOne, 10000000, uint256.NewInt(0), true)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.errContains)
} else {
s.Require().NoError(err)
s.Require().NotNil(bz)
tc.postCheck(bz)
}
})
}
}
func (s *WrappedA0giBaseTestSuite) TestMinterSupply() {
method := WrappedA0GIBaseFunctionMinterSupply
agency := s.wa0gibase.getAgency()
testCases := []struct {
name string
malleate func() []byte
postCheck func(bz []byte)
gas uint64
expErr bool
errContains string
}{
{
"non-empty",
func() []byte {
input, err := s.abi.Pack(
method,
s.signerOne,
)
s.Assert().NoError(err)
return input
},
func(data []byte) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
supply := out[0].(wrappeda0gibase.Supply)
fmt.Println(supply)
s.Require().Equal(supply.Cap, big.NewInt(8e18))
s.Require().Equal(supply.InitialSupply, big.NewInt(4e18))
s.Require().Equal(supply.Supply, big.NewInt(4e18+1e18))
// fmt.Println(wa0gi)
},
100000,
false,
"",
}, {
"empty",
func() []byte {
input, err := s.abi.Pack(
method,
s.signerTwo,
)
s.Assert().NoError(err)
return input
},
func(data []byte) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
supply := out[0].(wrappeda0gibase.Supply)
s.Require().Equal(supply.Cap.Bytes(), big.NewInt(0).Bytes())
s.Require().Equal(supply.InitialSupply.Bytes(), big.NewInt(0).Bytes())
s.Require().Equal(supply.Supply.Bytes(), big.NewInt(0).Bytes())
// fmt.Println(wa0gi)
},
100000,
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
// set minter cap
input, err := s.abi.Pack(
WrappedA0GIBaseFunctionSetMinterCap,
s.signerOne,
big.NewInt(8e18),
big.NewInt(4e18),
)
s.Assert().NoError(err)
_, err = s.runTx(input, agency, 10000000, uint256.NewInt(0), false)
s.Assert().NoError(err)
// mint
input, err = s.abi.Pack(
WrappedA0GIBaseFunctionMint,
s.signerOne,
big.NewInt(1e18),
)
s.Assert().NoError(err)
_, err = s.runTx(input, s.wa0gibase.getWA0GI(), 10000000, uint256.NewInt(0), false)
s.Assert().NoError(err)
bz, err := s.runTx(tc.malleate(), s.signerOne, 10000000, uint256.NewInt(0), true)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.errContains)
} else {
s.Require().NoError(err)
s.Require().NotNil(bz)
tc.postCheck(bz)
}
})
}
}
func (s *WrappedA0giBaseTestSuite) TestMint() {
method := WrappedA0GIBaseFunctionMint
agency := s.wa0gibase.getAgency()
testCases := []struct {
name string
malleate func() []byte
postCheck func()
gas uint64
expErr bool
errContains string
isSenderWA0GI bool
}{
{
"success",
func() []byte {
input, err := s.abi.Pack(
method,
s.signerOne,
big.NewInt(1e18),
)
s.Assert().NoError(err)
return input
},
func() {
supply, err := s.wa0gibase.getMinterSupply(s.evm, s.signerOne)
s.Assert().NoError(err)
s.Require().Equal(supply.Cap, big.NewInt(8e18))
s.Require().Equal(supply.InitialSupply, big.NewInt(4e18))
s.Require().Equal(supply.Supply, big.NewInt(4e18+1e18))
},
100000,
false,
"",
true,
}, {
"fail",
func() []byte {
input, err := s.abi.Pack(
method,
s.signerOne,
big.NewInt(9e18),
)
s.Assert().NoError(err)
return input
},
func() {},
100000,
true,
"insufficient mint cap",
true,
}, {
"invalid sender",
func() []byte {
input, err := s.abi.Pack(
method,
s.signerTwo,
big.NewInt(9e18),
)
s.Assert().NoError(err)
return input
},
func() {},
100000,
true,
"sender is not WA0GI",
false,
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
input, err := s.abi.Pack(
WrappedA0GIBaseFunctionSetMinterCap,
s.signerOne,
big.NewInt(8e18),
big.NewInt(4e18),
)
s.Assert().NoError(err)
_, err = s.runTx(input, agency, 10000000, uint256.NewInt(0), false)
s.Assert().NoError(err)
if tc.isSenderWA0GI {
_, err = s.runTx(tc.malleate(), s.wa0gibase.getWA0GI(), 10000000, uint256.NewInt(0), false)
} else {
_, err = s.runTx(tc.malleate(), s.signerTwo, 10000000, uint256.NewInt(0), false)
}
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.errContains)
} else {
s.Require().NoError(err)
tc.postCheck()
}
})
}
}
func (s *WrappedA0giBaseTestSuite) TestBurn() {
method := WrappedA0GIBaseFunctionBurn
agency := s.wa0gibase.getAgency()
testCases := []struct {
name string
malleate func() []byte
postCheck func()
gas uint64
expErr bool
errContains string
isSenderWA0GI bool
}{
{
"success",
func() []byte {
input, err := s.abi.Pack(
method,
s.signerOne,
big.NewInt(1e18),
)
s.Assert().NoError(err)
return input
},
func() {
supply, err := s.wa0gibase.getMinterSupply(s.evm, s.signerOne)
s.Assert().NoError(err)
s.Require().Equal(supply.Cap, big.NewInt(8e18))
s.Require().Equal(supply.InitialSupply, big.NewInt(4e18))
s.Require().Equal(supply.Supply, big.NewInt(3e18))
// fmt.Println(wa0gi)
},
100000,
false,
"",
true,
}, {
"fail",
func() []byte {
input, err := s.abi.Pack(
method,
s.signerOne,
big.NewInt(9e18),
)
s.Assert().NoError(err)
return input
},
func() {},
100000,
true,
"insufficient mint supply",
true,
}, {
"invalid sender",
func() []byte {
input, err := s.abi.Pack(
method,
s.signerTwo,
big.NewInt(9e18),
)
s.Assert().NoError(err)
return input
},
func() {},
100000,
true,
"sender is not WA0GI",
false,
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
input, err := s.abi.Pack(
WrappedA0GIBaseFunctionSetMinterCap,
s.signerOne,
big.NewInt(8e18),
big.NewInt(4e18),
)
s.Assert().NoError(err)
_, err = s.runTx(input, agency, 10000000, uint256.NewInt(0), false)
s.Assert().NoError(err)
if tc.isSenderWA0GI {
_, err = s.runTx(tc.malleate(), s.wa0gibase.getWA0GI(), 10000000, uint256.NewInt(0), false)
} else {
_, err = s.runTx(tc.malleate(), s.signerTwo, 10000000, uint256.NewInt(0), false)
}
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.errContains)
} else {
s.Require().NoError(err)
tc.postCheck()
}
})
}
}
func (s *WrappedA0giBaseTestSuite) TestSetMinterCap() {
agency := s.wa0gibase.getAgency()
testCases := []struct {
name string
caps []struct {
account common.Address
cap *big.Int
initialSupply *big.Int
}
}{
{
name: "success",
caps: []struct {
account common.Address
cap *big.Int
initialSupply *big.Int
}{
{
account: common.HexToAddress("0x0000000000000000000000000000000000000000"),
cap: big.NewInt(100000),
initialSupply: big.NewInt(50000),
},
{
account: common.HexToAddress("0x0000000000000000000000000000000000000001"),
cap: big.NewInt(200000),
initialSupply: big.NewInt(100000),
},
{
account: common.HexToAddress("0x0000000000000000000000000000000000000002"),
cap: big.NewInt(300000),
initialSupply: big.NewInt(150000),
},
{
account: common.HexToAddress("0x0000000000000000000000000000000000000003"),
cap: big.NewInt(400000),
initialSupply: big.NewInt(200000),
},
{
account: common.HexToAddress("0x0000000000000000000000000000000000000002"),
cap: big.NewInt(500000),
initialSupply: big.NewInt(250000),
},
{
account: common.HexToAddress("0x0000000000000000000000000000000000000001"),
cap: big.NewInt(600000),
initialSupply: big.NewInt(300000),
},
{
account: common.HexToAddress("0x0000000000000000000000000000000000000000"),
cap: big.NewInt(700000),
initialSupply: big.NewInt(350000),
},
},
},
}
s.Run("invalid authority", func() {
s.SetupTest()
input, err := s.abi.Pack(
WrappedA0GIBaseFunctionSetMinterCap,
s.signerOne,
big.NewInt(8e18),
big.NewInt(4e18),
)
s.Assert().NoError(err)
_, err = s.runTx(input, s.signerOne, 10000000, uint256.NewInt(0), false)
s.Require().Error(err)
s.Require().Contains(err.Error(), "sender is not agency")
})
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
c := make(map[common.Address]struct {
Cap *big.Int
InitialSupply *big.Int
})
for _, cap := range tc.caps {
input, err := s.abi.Pack(
WrappedA0GIBaseFunctionSetMinterCap,
cap.account,
cap.cap,
cap.initialSupply,
)
s.Assert().NoError(err)
_, err = s.runTx(input, agency, 10000000, uint256.NewInt(0), false)
s.Require().NoError(err)
supply, err := s.wa0gibase.getMinterSupply(s.evm, cap.account)
s.Require().NoError(err)
s.Require().Equal(supply.Cap, cap.cap)
s.Require().Equal(supply.InitialSupply, cap.initialSupply)
s.Require().Equal(supply.Supply, cap.initialSupply)
c[cap.account] = struct {
Cap *big.Int
InitialSupply *big.Int
}{
Cap: cap.cap,
InitialSupply: cap.initialSupply,
}
}
for account, cap := range c {
supply, err := s.wa0gibase.getMinterSupply(s.evm, account)
s.Require().NoError(err)
s.Require().Equal(supply.Cap, cap.Cap)
s.Require().Equal(supply.InitialSupply, cap.InitialSupply)
s.Require().Equal(supply.Supply, cap.InitialSupply)
}
})
}
}
type MintBurn struct {
IsMint bool
Minter common.Address
Amount *big.Int
Success bool
}
func (s *WrappedA0giBaseTestSuite) TestSetMintBurn() {
agency := s.wa0gibase.getAgency()
minter1 := common.HexToAddress("0x0000000000000000000000000000000000000001")
minter2 := common.HexToAddress("0x0000000000000000000000000000000000000002")
// set mint cap of minter 1 to 8 a0gi
input, err := s.abi.Pack(
WrappedA0GIBaseFunctionSetMinterCap,
minter1,
big.NewInt(8e18),
big.NewInt(0),
)
s.Assert().NoError(err)
_, err = s.runTx(input, agency, 10000000, uint256.NewInt(0), false)
s.Require().NoError(err)
// set mint cap of minter 2 to 5 a0gi
input, err = s.abi.Pack(
WrappedA0GIBaseFunctionSetMinterCap,
minter2,
big.NewInt(5e18),
big.NewInt(0),
)
s.Assert().NoError(err)
_, err = s.runTx(input, agency, 10000000, uint256.NewInt(0), false)
s.Require().NoError(err)
testCases := []MintBurn{
// #0, failed burn
{
IsMint: false,
Minter: minter1,
Amount: big.NewInt(1e18),
Success: false,
},
// #1, mint 5 a0gi by minter 1
{
IsMint: true,
Minter: minter1,
Amount: big.NewInt(5e18),
Success: true,
},
// #2, burn 0.5 a0gi by minter 1
{
IsMint: false,
Minter: minter1,
Amount: big.NewInt(5e17),
Success: true,
},
// #3, mint 0.7 a0gi by minter 2
{
IsMint: true,
Minter: minter2,
Amount: big.NewInt(7e17),
Success: true,
},
// #4, mint 2 a0gi by minter 2
{
IsMint: true,
Minter: minter2,
Amount: big.NewInt(2e18),
Success: true,
},
// #5, burn 0.3 a0gi by minter 2
{
IsMint: false,
Minter: minter1,
Amount: big.NewInt(3e17),
Success: true,
},
// #6, failed to mint 4 a0gi by minter 1
{
IsMint: true,
Minter: minter1,
Amount: big.NewInt(4e18),
Success: false,
},
// #7, mint 3.5 a0gi by minter 1
{
IsMint: true,
Minter: minter1,
Amount: big.NewInt(3e18 + 5e17),
Success: true,
},
}
minted := big.NewInt(0)
supplied := make(map[common.Address]*big.Int)
for _, c := range testCases {
if c.IsMint {
input, err = s.abi.Pack(
WrappedA0GIBaseFunctionMint,
c.Minter,
c.Amount,
)
s.Assert().NoError(err)
_, err = s.runTx(input, s.wa0gibase.getWA0GI(), 10000000, uint256.NewInt(0), false)
} else {
input, err = s.abi.Pack(
WrappedA0GIBaseFunctionBurn,
c.Minter,
c.Amount,
)
s.Assert().NoError(err)
_, err = s.runTx(input, s.wa0gibase.getWA0GI(), 10000000, uint256.NewInt(0), false)
}
if c.Success {
if c.IsMint {
minted.Add(minted, c.Amount)
if amt, ok := supplied[c.Minter]; ok {
amt.Add(amt, c.Amount)
} else {
supplied[c.Minter] = new(big.Int).Set(c.Amount)
}
} else {
minted.Sub(minted, c.Amount)
if amt, ok := supplied[c.Minter]; ok {
amt.Sub(amt, c.Amount)
} else {
supplied[c.Minter] = new(big.Int).Set(c.Amount)
}
}
s.Require().NoError(err)
supply, err := s.wa0gibase.getMinterSupply(s.evm, c.Minter)
s.Require().NoError(err)
s.Require().Equal(supplied[c.Minter].Bytes(), supply.Supply.Bytes())
} else {
s.Require().Error(err)
}
balance := s.evm.StateDB.GetBalance(s.wa0gibase.getWA0GI())
s.Require().Equal(balance.ToBig().Bytes(), minted.Bytes())
}
}
func TestWrappedA0giBaseTestSuite(t *testing.T) {
suite.Run(t, new(WrappedA0giBaseTestSuite))
}

2
go.mod
View file

@ -141,6 +141,8 @@ require (
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.36.0 // indirect

4
go.sum
View file

@ -514,6 +514,10 @@ github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=

View file

@ -31,9 +31,15 @@ import (
type precompileContract struct{}
func (precompileContract) Address() common.Address {
return common.BytesToAddress([]byte{0x1})
}
func (p *precompileContract) RequiredGas(input []byte) uint64 { return 0 }
func (p *precompileContract) Run(input []byte) ([]byte, error) { return nil, nil }
func (p *precompileContract) Run(evm *vm.EVM, contract *vm.Contract, readonly bool) ([]byte, error) {
return nil, nil
}
func TestStateOverrideMovePrecompile(t *testing.T) {
db := state.NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil), nil)

View file

@ -76,7 +76,7 @@ func fuzz(id byte, data []byte) int {
}
cpy := make([]byte, len(data))
copy(cpy, data)
_, err := precompile.Run(cpy)
_, err := precompile.Run(nil, &vm.Contract{Input: cpy}, true)
if !bytes.Equal(cpy, data) {
panic(fmt.Sprintf("input data modified, precompile %d: %x %x", id, data, cpy))
}