mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 07:06:42 +00:00
feat: dasigners precompile
This commit is contained in:
parent
eca91aa939
commit
c8b9013ac6
31 changed files with 6574 additions and 1 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -43,3 +43,6 @@ profile.cov
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
tests/spec-tests/
|
tests/spec-tests/
|
||||||
|
|
||||||
|
# Precompiles
|
||||||
|
core/vm/Precompiles/interfaces/node_modules
|
||||||
|
|
|
||||||
166
common/bn254util/bn254util.go
Normal file
166
common/bn254util/bn254util.go
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ package common
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
@ -486,3 +487,9 @@ func (b PrettyBytes) TerminalString() string {
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%#x...%x (%dB)", b[:3], b[len(b)-3:], len(b))
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
|
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/fp"
|
||||||
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
"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/common"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -68,6 +69,43 @@ func WithCustomPrecompiles(base PrecompiledContracts) PrecompiledContracts {
|
||||||
return result
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
return method, args, nil
|
||||||
|
}
|
||||||
|
|
||||||
// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum
|
// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum
|
||||||
// contracts used in the Frontier and Homestead releases.
|
// contracts used in the Frontier and Homestead releases.
|
||||||
var PrecompiledContractsHomestead = WithCustomPrecompiles(PrecompiledContracts{
|
var PrecompiledContractsHomestead = WithCustomPrecompiles(PrecompiledContracts{
|
||||||
|
|
|
||||||
633
core/vm/dasigners.go
Normal file
633
core/vm/dasigners.go
Normal file
|
|
@ -0,0 +1,633 @@
|
||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"math/big"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"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/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm/precompiles"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm/precompiles/dasigners"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/vmihailenco/msgpack/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
requiredGasMax uint64 = 1000_000_000
|
||||||
|
|
||||||
|
DASignersFunctionParams = "params"
|
||||||
|
DASignersFunctionEpochNumber = "epochNumber"
|
||||||
|
DASignersFunctionQuorumCount = "quorumCount"
|
||||||
|
DASignersFunctionGetSigner = "getSigner"
|
||||||
|
DASignersFunctionGetQuorum = "getQuorum"
|
||||||
|
DASignersFunctionGetQuorumRow = "getQuorumRow"
|
||||||
|
DASignersFunctionRegisterSigner = "registerSigner"
|
||||||
|
DASignersFunctionUpdateSocket = "updateSocket"
|
||||||
|
DASignersFunctionRegisterNextEpoch = "registerNextEpoch"
|
||||||
|
DASignersFunctionGetAggPkG1 = "getAggPkG1"
|
||||||
|
DASignersFunctionIsSigner = "isSigner"
|
||||||
|
DASignersFunctionRegisteredEpoch = "registeredEpoch"
|
||||||
|
DASignersFunctionMakeEpoch = "makeEpoch"
|
||||||
|
)
|
||||||
|
|
||||||
|
var RequiredGasBasic = map[string]uint64{
|
||||||
|
DASignersFunctionParams: 1_000,
|
||||||
|
DASignersFunctionEpochNumber: 1_000,
|
||||||
|
DASignersFunctionQuorumCount: 1_000,
|
||||||
|
DASignersFunctionGetSigner: 100_000,
|
||||||
|
DASignersFunctionGetQuorum: 100_000,
|
||||||
|
DASignersFunctionGetQuorumRow: 10_000,
|
||||||
|
DASignersFunctionRegisterSigner: 100_000,
|
||||||
|
DASignersFunctionUpdateSocket: 50_000,
|
||||||
|
DASignersFunctionRegisterNextEpoch: 100_000,
|
||||||
|
DASignersFunctionGetAggPkG1: 1_000_000,
|
||||||
|
DASignersFunctionIsSigner: 10_000,
|
||||||
|
DASignersFunctionRegisteredEpoch: 10_000,
|
||||||
|
DASignersFunctionMakeEpoch: 100_000,
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
NewSignerEvent = "NewSigner"
|
||||||
|
SocketUpdatedEvent = "SocketUpdated"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ StatefulPrecompiledContract = &DASignersPrecompile{}
|
||||||
|
|
||||||
|
type DASignersPrecompile struct {
|
||||||
|
abi abi.ABI
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDASignersPrecompile() (*DASignersPrecompile, error) {
|
||||||
|
abi, err := abi.JSON(strings.NewReader(dasigners.DASignersABI))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &DASignersPrecompile{
|
||||||
|
abi: abi,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address implements vm.PrecompiledContract.
|
||||||
|
func (d *DASignersPrecompile) Address() common.Address {
|
||||||
|
return common.HexToAddress("0x0000000000000000000000000000000000001000")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequiredGas implements vm.PrecompiledContract.
|
||||||
|
func (d *DASignersPrecompile) RequiredGas(input []byte) uint64 {
|
||||||
|
method, err := d.abi.MethodById(input[:4])
|
||||||
|
if err != nil {
|
||||||
|
return requiredGasMax
|
||||||
|
}
|
||||||
|
if gas, ok := RequiredGasBasic[method.Name]; ok {
|
||||||
|
return gas
|
||||||
|
}
|
||||||
|
return requiredGasMax
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) IsTx(method string) bool {
|
||||||
|
switch method {
|
||||||
|
case DASignersFunctionUpdateSocket,
|
||||||
|
DASignersFunctionRegisterSigner,
|
||||||
|
DASignersFunctionRegisterNextEpoch:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) Abi() *abi.ABI {
|
||||||
|
return &d.abi
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run implements vm.PrecompiledContract.
|
||||||
|
func (d *DASignersPrecompile) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||||
|
method, args, err := InitializeStatefulPrecompileCall(d, evm, contract, readonly)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var bz []byte
|
||||||
|
switch method.Name {
|
||||||
|
// queries
|
||||||
|
case DASignersFunctionParams:
|
||||||
|
bz, err = d.Params(evm, method, args)
|
||||||
|
case DASignersFunctionEpochNumber:
|
||||||
|
bz, err = d.EpochNumber(evm, method, args)
|
||||||
|
case DASignersFunctionQuorumCount:
|
||||||
|
bz, err = d.QuorumCount(evm, method, args)
|
||||||
|
case DASignersFunctionGetSigner:
|
||||||
|
bz, err = d.GetSigner(evm, method, args)
|
||||||
|
case DASignersFunctionGetQuorum:
|
||||||
|
bz, err = d.GetQuorum(evm, method, args)
|
||||||
|
case DASignersFunctionGetQuorumRow:
|
||||||
|
bz, err = d.GetQuorumRow(evm, method, args)
|
||||||
|
case DASignersFunctionGetAggPkG1:
|
||||||
|
bz, err = d.GetAggPkG1(evm, method, args)
|
||||||
|
case DASignersFunctionIsSigner:
|
||||||
|
bz, err = d.IsSigner(evm, method, args)
|
||||||
|
case DASignersFunctionRegisteredEpoch:
|
||||||
|
bz, err = d.RegisteredEpoch(evm, method, args)
|
||||||
|
// txs
|
||||||
|
case DASignersFunctionRegisterSigner:
|
||||||
|
bz, err = d.RegisterSigner(evm, contract, method, args)
|
||||||
|
case DASignersFunctionRegisterNextEpoch:
|
||||||
|
bz, err = d.RegisterNextEpoch(evm, contract, method, args)
|
||||||
|
case DASignersFunctionUpdateSocket:
|
||||||
|
bz, err = d.UpdateSocket(evm, contract, method, args)
|
||||||
|
case DASignersFunctionMakeEpoch:
|
||||||
|
bz, err = d.MakeEpoch(evm, contract, method, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bz, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) EmitNewSignerEvent(evm *EVM, signer dasigners.IDASignersSignerDetail) error {
|
||||||
|
event := d.abi.Events[NewSignerEvent]
|
||||||
|
quries := make([]interface{}, 2)
|
||||||
|
quries[0] = event.ID
|
||||||
|
quries[1] = signer.Signer
|
||||||
|
topics, err := abi.MakeTopics(quries)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
arguments := abi.Arguments{event.Inputs[1], event.Inputs[2]}
|
||||||
|
b, err := arguments.Pack(signer.PkG1, signer.PkG2)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
evm.StateDB.AddLog(&types.Log{
|
||||||
|
Address: d.Address(),
|
||||||
|
Topics: topics[0],
|
||||||
|
Data: b,
|
||||||
|
BlockNumber: evm.Context.BlockNumber.Uint64(),
|
||||||
|
})
|
||||||
|
return d.EmitSocketUpdatedEvent(evm, signer.Signer, signer.Socket)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) EmitSocketUpdatedEvent(evm *EVM, signer common.Address, socket string) error {
|
||||||
|
event := d.abi.Events[SocketUpdatedEvent]
|
||||||
|
quries := make([]interface{}, 2)
|
||||||
|
quries[0] = event.ID
|
||||||
|
quries[1] = signer
|
||||||
|
topics, err := abi.MakeTopics(quries)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
arguments := abi.Arguments{event.Inputs[1]}
|
||||||
|
b, err := arguments.Pack(socket)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
evm.StateDB.AddLog(&types.Log{
|
||||||
|
Address: d.Address(),
|
||||||
|
Topics: topics[0],
|
||||||
|
Data: b,
|
||||||
|
BlockNumber: evm.Context.BlockNumber.Uint64(),
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Ballot struct {
|
||||||
|
account common.Address
|
||||||
|
content []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) MakeEpoch(
|
||||||
|
evm *EVM,
|
||||||
|
contract *Contract,
|
||||||
|
method *abi.Method,
|
||||||
|
args []interface{},
|
||||||
|
) ([]byte, error) {
|
||||||
|
if len(args) != 0 {
|
||||||
|
return nil, ErrExecutionReverted
|
||||||
|
}
|
||||||
|
params := d.params()
|
||||||
|
epoch := d.epochNumber(evm)
|
||||||
|
epochBlock := d.epochBlock(evm, epoch)
|
||||||
|
blockHeight := evm.Context.BlockNumber.Uint64()
|
||||||
|
if epochBlock > 0 && blockHeight < epochBlock+params.EpochBlocks.Uint64() {
|
||||||
|
// not yet to the next epoch
|
||||||
|
return method.Outputs.Pack()
|
||||||
|
}
|
||||||
|
// new epoch
|
||||||
|
epoch += 1
|
||||||
|
cnt := d.epochRegistration(evm, epoch)
|
||||||
|
ballots := make([]Ballot, cnt)
|
||||||
|
for index := range cnt {
|
||||||
|
account := d.epochRegisteredSigner(evm, epoch, index)
|
||||||
|
sigHash, _ := d.getRegistration(evm, epoch, account)
|
||||||
|
ballots[index] = Ballot{
|
||||||
|
account: account,
|
||||||
|
content: sigHash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: calculate ballots based on staked amount
|
||||||
|
sort.Slice(ballots, func(i, j int) bool {
|
||||||
|
return bytes.Compare(ballots[i].content, ballots[j].content) < 0
|
||||||
|
})
|
||||||
|
|
||||||
|
quorums := make([][]common.Address, 0)
|
||||||
|
encodedSlices := params.EncodedSlices.Uint64()
|
||||||
|
maxQuorums := params.MaxQuorums.Uint64()
|
||||||
|
if len(ballots) >= int(encodedSlices) {
|
||||||
|
for i := 0; i+int(encodedSlices) <= len(ballots); i += int(encodedSlices) {
|
||||||
|
if int(maxQuorums) <= len(quorums) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
quorum := make([]common.Address, encodedSlices)
|
||||||
|
for j := 0; j < int(encodedSlices); j += 1 {
|
||||||
|
quorum[j] = ballots[i+j].account
|
||||||
|
}
|
||||||
|
quorums = append(quorums, quorum)
|
||||||
|
}
|
||||||
|
if len(ballots)%int(encodedSlices) != 0 && int(maxQuorums) > len(quorums) {
|
||||||
|
quorum := make([]common.Address, 0)
|
||||||
|
for j := len(ballots) - int(encodedSlices); j < len(ballots); j += 1 {
|
||||||
|
quorum = append(quorum, ballots[j].account)
|
||||||
|
}
|
||||||
|
quorums = append(quorums, quorum)
|
||||||
|
}
|
||||||
|
} else if len(ballots) > 0 {
|
||||||
|
quorum := make([]common.Address, encodedSlices)
|
||||||
|
n := len(ballots)
|
||||||
|
for i := 0; i < int(encodedSlices); i += 1 {
|
||||||
|
quorum[i] = ballots[i%n].account
|
||||||
|
}
|
||||||
|
quorums = append(quorums, quorum)
|
||||||
|
}
|
||||||
|
|
||||||
|
// save quorums
|
||||||
|
for index, quorum := range quorums {
|
||||||
|
b, err := msgpack.Marshal(quorum)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
StoreBytes(evm.StateDB, d.Address(), dasigners.QuorumKey(epoch+1, uint64(index)), b)
|
||||||
|
}
|
||||||
|
// save epoch number & block height
|
||||||
|
evm.StateDB.SetState(d.Address(), dasigners.EpochNumberKey(), common.BigToHash(big.NewInt(int64(epoch))))
|
||||||
|
evm.StateDB.SetState(d.Address(), dasigners.EpochBlockKey(epoch), common.BigToHash(big.NewInt(int64(blockHeight))))
|
||||||
|
return method.Outputs.Pack()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) setSigner(evm *EVM, signer dasigners.IDASignersSignerDetail) error {
|
||||||
|
b, err := msgpack.Marshal(signer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
StoreBytes(evm.StateDB, d.Address(), dasigners.SignerKey(signer.Signer), b)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) RegisterSigner(
|
||||||
|
evm *EVM,
|
||||||
|
contract *Contract,
|
||||||
|
method *abi.Method,
|
||||||
|
args []interface{},
|
||||||
|
) ([]byte, error) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return nil, ErrExecutionReverted
|
||||||
|
}
|
||||||
|
signer := args[0].(dasigners.IDASignersSignerDetail)
|
||||||
|
signature := dasigners.SerializeG1(args[1].(dasigners.BN254G1Point))
|
||||||
|
// validation
|
||||||
|
if evm.Origin != signer.Signer {
|
||||||
|
return nil, dasigners.ErrInvalidSender
|
||||||
|
}
|
||||||
|
if contract.caller != evm.Origin {
|
||||||
|
return nil, precompiles.ErrSenderNotOrigin
|
||||||
|
}
|
||||||
|
// execute
|
||||||
|
// validate sender
|
||||||
|
// TODO: check staked
|
||||||
|
_, found, err := d.getSigner(evm, signer.Signer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if found {
|
||||||
|
return nil, dasigners.ErrSignerExists
|
||||||
|
}
|
||||||
|
// validate signature
|
||||||
|
chainID := evm.chainConfig.ChainID
|
||||||
|
hash := dasigners.PubkeyRegistrationHash(signer.Signer, chainID)
|
||||||
|
if !dasigners.ValidateSignature(signer, hash, bn254util.DeserializeG1(signature)) {
|
||||||
|
return nil, dasigners.ErrInvalidSignature
|
||||||
|
}
|
||||||
|
// save signer
|
||||||
|
if err := d.setSigner(evm, signer); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// emit events
|
||||||
|
err = d.EmitNewSignerEvent(evm, signer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return method.Outputs.Pack()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) epochRegistration(evm *EVM, epoch uint64) uint64 {
|
||||||
|
return evm.StateDB.GetState(d.Address(), dasigners.EpochRegistrationKey(epoch)).Big().Uint64()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) epochRegisteredSigner(evm *EVM, epoch uint64, index uint64) common.Address {
|
||||||
|
h := evm.StateDB.GetState(d.Address(), dasigners.EpochRegisteredSignerKey(epoch, index))
|
||||||
|
return common.Address(h[12:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) storeRegistration(evm *EVM, epoch uint64, signer common.Address, signature []byte) error {
|
||||||
|
if _, found := d.getRegistration(evm, epoch, signer); found {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// save signature hash
|
||||||
|
evm.StateDB.SetState(d.Address(), dasigners.RegistrationKey(epoch, signer), crypto.Keccak256Hash(signature))
|
||||||
|
// increment epoch registration count
|
||||||
|
registration := d.epochRegistration(evm, epoch)
|
||||||
|
evm.StateDB.SetState(d.Address(), dasigners.EpochRegistrationKey(epoch), common.BigToHash(big.NewInt(int64(registration+1))))
|
||||||
|
// save registered signer address
|
||||||
|
evm.StateDB.SetState(d.Address(), dasigners.EpochRegisteredSignerKey(epoch, registration), common.BytesToHash(signer.Bytes()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) RegisterNextEpoch(
|
||||||
|
evm *EVM,
|
||||||
|
contract *Contract,
|
||||||
|
method *abi.Method,
|
||||||
|
args []interface{},
|
||||||
|
) ([]byte, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrExecutionReverted
|
||||||
|
}
|
||||||
|
signature := dasigners.SerializeG1(args[0].(dasigners.BN254G1Point))
|
||||||
|
// validation
|
||||||
|
if contract.caller != evm.Origin {
|
||||||
|
return nil, precompiles.ErrSenderNotOrigin
|
||||||
|
}
|
||||||
|
// execute
|
||||||
|
// get signer
|
||||||
|
// TODO: check staked
|
||||||
|
signer, found, err := d.getSigner(evm, contract.caller)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return nil, dasigners.ErrSignerNotFound
|
||||||
|
}
|
||||||
|
// validate signature
|
||||||
|
epochNumber := d.epochNumber(evm)
|
||||||
|
chainID := evm.chainConfig.ChainID
|
||||||
|
hash := dasigners.EpochRegistrationHash(contract.caller, epochNumber+1, chainID)
|
||||||
|
if !dasigners.ValidateSignature(signer, hash, bn254util.DeserializeG1(signature)) {
|
||||||
|
return nil, dasigners.ErrInvalidSignature
|
||||||
|
}
|
||||||
|
// save registration
|
||||||
|
if err := d.storeRegistration(evm, epochNumber+1, contract.caller, signature); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return method.Outputs.Pack()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) UpdateSocket(
|
||||||
|
evm *EVM,
|
||||||
|
contract *Contract,
|
||||||
|
method *abi.Method,
|
||||||
|
args []interface{},
|
||||||
|
) ([]byte, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrExecutionReverted
|
||||||
|
}
|
||||||
|
socket := args[0].(string)
|
||||||
|
// validation
|
||||||
|
if contract.caller != evm.Origin {
|
||||||
|
return nil, precompiles.ErrSenderNotOrigin
|
||||||
|
}
|
||||||
|
// execute
|
||||||
|
signer, found, err := d.getSigner(evm, contract.caller)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return nil, dasigners.ErrSignerNotFound
|
||||||
|
}
|
||||||
|
signer.Socket = socket
|
||||||
|
if err := d.setSigner(evm, signer); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// emit events
|
||||||
|
err = d.EmitSocketUpdatedEvent(evm, contract.caller, socket)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return method.Outputs.Pack()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) params() dasigners.IDASignersParams {
|
||||||
|
return dasigners.IDASignersParams{
|
||||||
|
TokensPerVote: big.NewInt(10),
|
||||||
|
MaxVotesPerSigner: big.NewInt(1024),
|
||||||
|
MaxQuorums: big.NewInt(10),
|
||||||
|
EpochBlocks: big.NewInt(5760),
|
||||||
|
EncodedSlices: big.NewInt(3072),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) Params(evm *EVM, method *abi.Method, _ []interface{}) ([]byte, error) {
|
||||||
|
return method.Outputs.Pack(d.params())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) epochBlock(evm *EVM, epoch uint64) uint64 {
|
||||||
|
return evm.StateDB.GetState(d.Address(), dasigners.EpochBlockKey(epoch)).Big().Uint64()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) epochNumber(evm *EVM) uint64 {
|
||||||
|
return evm.StateDB.GetState(d.Address(), dasigners.EpochNumberKey()).Big().Uint64()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) EpochNumber(evm *EVM, method *abi.Method, args []interface{}) ([]byte, error) {
|
||||||
|
if len(args) != 0 {
|
||||||
|
return nil, ErrExecutionReverted
|
||||||
|
}
|
||||||
|
return method.Outputs.Pack(big.NewInt(int64(d.epochNumber(evm))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) quorumCount(evm *EVM, epochNumber uint64) uint64 {
|
||||||
|
return evm.StateDB.GetState(d.Address(), dasigners.QuorumCountKey(epochNumber)).Big().Uint64()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) QuorumCount(evm *EVM, method *abi.Method, args []interface{}) ([]byte, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrExecutionReverted
|
||||||
|
}
|
||||||
|
epochNumber := args[0].(*big.Int).Uint64()
|
||||||
|
return method.Outputs.Pack(big.NewInt(int64(d.quorumCount(evm, epochNumber))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) getSigner(evm *EVM, account common.Address) (dasigners.IDASignersSignerDetail, bool, error) {
|
||||||
|
b := LoadBytes(evm.StateDB, d.Address(), dasigners.SignerKey(account))
|
||||||
|
if len(b) == 0 {
|
||||||
|
return dasigners.IDASignersSignerDetail{}, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var signer dasigners.IDASignersSignerDetail
|
||||||
|
err := msgpack.Unmarshal(b, &signer)
|
||||||
|
if err != nil {
|
||||||
|
return dasigners.IDASignersSignerDetail{}, false, err
|
||||||
|
}
|
||||||
|
return signer, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) GetSigner(evm *EVM, method *abi.Method, args []interface{}) ([]byte, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrExecutionReverted
|
||||||
|
}
|
||||||
|
accounts := args[0].([]common.Address)
|
||||||
|
signers := make([]dasigners.IDASignersSignerDetail, len(accounts))
|
||||||
|
for i, account := range accounts {
|
||||||
|
signer, found, err := d.getSigner(evm, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return nil, dasigners.ErrSignerNotFound
|
||||||
|
}
|
||||||
|
signers[i] = signer
|
||||||
|
}
|
||||||
|
return method.Outputs.Pack(signers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) IsSigner(evm *EVM, method *abi.Method, args []interface{}) ([]byte, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrExecutionReverted
|
||||||
|
}
|
||||||
|
account := args[0].(common.Address)
|
||||||
|
_, found, err := d.getSigner(evm, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return method.Outputs.Pack(found)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) getRegistration(evm *EVM, epoch uint64, account common.Address) ([]byte, bool) {
|
||||||
|
h := evm.StateDB.GetState(d.Address(), dasigners.RegistrationKey(epoch, account))
|
||||||
|
if h == (common.Hash{}) {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return h.Bytes(), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) RegisteredEpoch(evm *EVM, method *abi.Method, args []interface{}) ([]byte, error) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return nil, ErrExecutionReverted
|
||||||
|
}
|
||||||
|
account := args[0].(common.Address)
|
||||||
|
epoch := args[1].(*big.Int).Uint64()
|
||||||
|
_, found := d.getRegistration(evm, epoch, account)
|
||||||
|
return method.Outputs.Pack(found)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) getQuorum(evm *EVM, epochNumber uint64, quorumId uint64) ([]common.Address, error) {
|
||||||
|
if d.quorumCount(evm, epochNumber) <= quorumId {
|
||||||
|
return nil, dasigners.ErrQuorumIdOutOfBound
|
||||||
|
}
|
||||||
|
if d.epochNumber(evm) < epochNumber {
|
||||||
|
return nil, dasigners.ErrEpochOutOfBound
|
||||||
|
}
|
||||||
|
b := LoadBytes(evm.StateDB, d.Address(), dasigners.QuorumKey(epochNumber, quorumId))
|
||||||
|
var quorum []common.Address
|
||||||
|
err := msgpack.Unmarshal(b, &quorum)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return quorum, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) GetQuorum(evm *EVM, method *abi.Method, args []interface{}) ([]byte, error) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return nil, ErrExecutionReverted
|
||||||
|
}
|
||||||
|
epochNumber := args[0].(*big.Int).Uint64()
|
||||||
|
quorumId := args[1].(*big.Int).Uint64()
|
||||||
|
quorum, err := d.getQuorum(evm, epochNumber, quorumId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return method.Outputs.Pack(quorum)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) GetQuorumRow(evm *EVM, method *abi.Method, args []interface{}) ([]byte, error) {
|
||||||
|
if len(args) != 3 {
|
||||||
|
return nil, ErrExecutionReverted
|
||||||
|
}
|
||||||
|
epochNumber := args[0].(*big.Int).Uint64()
|
||||||
|
quorumId := args[1].(*big.Int).Uint64()
|
||||||
|
rowIndex := args[2].(uint32)
|
||||||
|
quorum, err := d.getQuorum(evm, epochNumber, quorumId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if int(rowIndex) >= len(quorum) {
|
||||||
|
return nil, dasigners.ErrRowIdOfBound
|
||||||
|
}
|
||||||
|
return method.Outputs.Pack(quorum[rowIndex])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) getAggPkG1(
|
||||||
|
evm *EVM,
|
||||||
|
epochNumber uint64,
|
||||||
|
quorumId uint64,
|
||||||
|
quorumBitmap []byte,
|
||||||
|
) (dasigners.BN254G1Point, *big.Int, *big.Int, error) {
|
||||||
|
quorum, err := d.getQuorum(evm, epochNumber, quorumId)
|
||||||
|
if err != nil {
|
||||||
|
return dasigners.BN254G1Point{}, nil, nil, err
|
||||||
|
}
|
||||||
|
if (len(quorum)+7)/8 != len(quorumBitmap) {
|
||||||
|
return dasigners.BN254G1Point{}, nil, nil, dasigners.ErrQuorumBitmapLengthMismatch
|
||||||
|
}
|
||||||
|
aggPubkeyG1 := new(bn254.G1Affine)
|
||||||
|
hit := 0
|
||||||
|
added := make(map[common.Address]struct{})
|
||||||
|
for i, signer := range quorum {
|
||||||
|
if _, ok := added[signer]; ok {
|
||||||
|
hit += 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b := quorumBitmap[i/8] & (1 << (i % 8))
|
||||||
|
if b == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
hit += 1
|
||||||
|
added[signer] = struct{}{}
|
||||||
|
signer, found, err := d.getSigner(evm, signer)
|
||||||
|
if err != nil {
|
||||||
|
return dasigners.BN254G1Point{}, nil, nil, err
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return dasigners.BN254G1Point{}, nil, nil, dasigners.ErrSignerNotFound
|
||||||
|
}
|
||||||
|
aggPubkeyG1.Add(aggPubkeyG1, bn254util.DeserializeG1(dasigners.SerializeG1(signer.PkG1)))
|
||||||
|
}
|
||||||
|
return dasigners.NewBN254G1Point(bn254util.SerializeG1(aggPubkeyG1)), big.NewInt(int64(len(quorum))), big.NewInt(int64(hit)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DASignersPrecompile) GetAggPkG1(evm *EVM, method *abi.Method, args []interface{}) ([]byte, error) {
|
||||||
|
if len(args) != 3 {
|
||||||
|
return nil, ErrExecutionReverted
|
||||||
|
}
|
||||||
|
epochNumber := args[0].(*big.Int).Uint64()
|
||||||
|
quorumId := args[1].(*big.Int).Uint64()
|
||||||
|
quorumBitmap := args[2].([]byte)
|
||||||
|
aggPkG1, total, hit, err := d.getAggPkG1(evm, epochNumber, quorumId, quorumBitmap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return method.Outputs.Pack(aggPkG1, total, hit)
|
||||||
|
}
|
||||||
882
core/vm/precompiles/dasigners/contract.go
Normal file
882
core/vm/precompiles/dasigners/contract.go
Normal file
File diff suppressed because one or more lines are too long
14
core/vm/precompiles/dasigners/errors.go
Normal file
14
core/vm/precompiles/dasigners/errors.go
Normal 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")
|
||||||
|
)
|
||||||
63
core/vm/precompiles/dasigners/signature.go
Normal file
63
core/vm/precompiles/dasigners/signature.go
Normal 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
|
||||||
|
}
|
||||||
87
core/vm/precompiles/dasigners/types.go
Normal file
87
core/vm/precompiles/dasigners/types.go
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
package dasigners
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
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} // signer => signature hash
|
||||||
|
quorumCountKey = []byte{0x03} // epoch => quorum count
|
||||||
|
epochNumberKey = []byte{0x04} // epoch number
|
||||||
|
epochBlockKey = []byte{0x05} // epoch number => block number
|
||||||
|
epochRegistrationKey = []byte{0x06} // epoch number => registration count
|
||||||
|
epochRegisteredSignerKey = []byte{0x07} // epoch number => index => signer
|
||||||
|
)
|
||||||
|
|
||||||
|
func SignerKey(account common.Address) common.Hash {
|
||||||
|
return crypto.Keccak256Hash(append(quorumCountKey, 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 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)...))
|
||||||
|
}
|
||||||
7
core/vm/precompiles/errors.go
Normal file
7
core/vm/precompiles/errors.go
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
package precompiles
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrSenderNotOrigin = errors.New("sender not origin")
|
||||||
|
)
|
||||||
18
core/vm/precompiles/interfaces/.solhint.json
Normal file
18
core/vm/precompiles/interfaces/.solhint.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
475
core/vm/precompiles/interfaces/abis/IDASigners.json
Normal file
475
core/vm/precompiles/interfaces/abis/IDASigners.json
Normal file
|
|
@ -0,0 +1,475 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"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": [
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"internalType": "uint256",
|
||||||
|
"name": "X",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "uint256",
|
||||||
|
"name": "Y",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"internalType": "struct BN254.G1Point",
|
||||||
|
"name": "_signature",
|
||||||
|
"type": "tuple"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
87
core/vm/precompiles/interfaces/abis/IWrappedA0GIBase.json
Normal file
87
core/vm/precompiles/interfaces/abis/IWrappedA0GIBase.json
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"_format": "hh-sol-dbg-1",
|
||||||
|
"buildInfo": "../../build-info/377589efa765dec208356377872d3755.json"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"_format": "hh-sol-artifact-1",
|
||||||
|
"contractName": "BN254",
|
||||||
|
"sourceName": "contracts/IDASigners.sol",
|
||||||
|
"abi": [],
|
||||||
|
"bytecode": "0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220abc43d74fec9de74f3b7a60ee2e0337c23369163d8fb7a1e1cb0037a16d8abfb64736f6c63430008140033",
|
||||||
|
"deployedBytecode": "0x73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220abc43d74fec9de74f3b7a60ee2e0337c23369163d8fb7a1e1cb0037a16d8abfb64736f6c63430008140033",
|
||||||
|
"linkReferences": {},
|
||||||
|
"deployedLinkReferences": {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"_format": "hh-sol-dbg-1",
|
||||||
|
"buildInfo": "../../build-info/377589efa765dec208356377872d3755.json"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,484 @@
|
||||||
|
{
|
||||||
|
"_format": "hh-sol-artifact-1",
|
||||||
|
"contractName": "IDASigners",
|
||||||
|
"sourceName": "contracts/IDASigners.sol",
|
||||||
|
"abi": [
|
||||||
|
{
|
||||||
|
"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": [
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"internalType": "uint256",
|
||||||
|
"name": "X",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "uint256",
|
||||||
|
"name": "Y",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"internalType": "struct BN254.G1Point",
|
||||||
|
"name": "_signature",
|
||||||
|
"type": "tuple"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bytecode": "0x",
|
||||||
|
"deployedBytecode": "0x",
|
||||||
|
"linkReferences": {},
|
||||||
|
"deployedLinkReferences": {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"_format": "hh-sol-dbg-1",
|
||||||
|
"buildInfo": "../../build-info/c79ff14473359160d0c7a65892d5cb52.json"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
{
|
||||||
|
"_format": "hh-sol-artifact-1",
|
||||||
|
"contractName": "IWrappedA0GIBase",
|
||||||
|
"sourceName": "contracts/IWrappedA0GIBase.sol",
|
||||||
|
"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": "struct Supply",
|
||||||
|
"name": "",
|
||||||
|
"type": "tuple"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bytecode": "0x",
|
||||||
|
"deployedBytecode": "0x",
|
||||||
|
"linkReferences": {},
|
||||||
|
"deployedLinkReferences": {}
|
||||||
|
}
|
||||||
78
core/vm/precompiles/interfaces/build/cache/solidity-files-cache.json
vendored
Normal file
78
core/vm/precompiles/interfaces/build/cache/solidity-files-cache.json
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
{
|
||||||
|
"_format": "hh-sol-cache-2",
|
||||||
|
"files": {
|
||||||
|
"/Users/wangfan/Project/0g-geth/core/vm/precompiles/interfaces/contracts/IDASigners.sol": {
|
||||||
|
"lastModificationDate": 1743021984706,
|
||||||
|
"contentHash": "dbe903452c6de71caa950fdb306027b3",
|
||||||
|
"sourceName": "contracts/IDASigners.sol",
|
||||||
|
"solcConfig": {
|
||||||
|
"version": "0.8.20",
|
||||||
|
"settings": {
|
||||||
|
"evmVersion": "istanbul",
|
||||||
|
"optimizer": {
|
||||||
|
"enabled": true,
|
||||||
|
"runs": 200
|
||||||
|
},
|
||||||
|
"outputSelection": {
|
||||||
|
"*": {
|
||||||
|
"*": [
|
||||||
|
"abi",
|
||||||
|
"evm.bytecode",
|
||||||
|
"evm.deployedBytecode",
|
||||||
|
"evm.methodIdentifiers",
|
||||||
|
"metadata"
|
||||||
|
],
|
||||||
|
"": [
|
||||||
|
"ast"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"imports": [],
|
||||||
|
"versionPragmas": [
|
||||||
|
">=0.8.0"
|
||||||
|
],
|
||||||
|
"artifacts": [
|
||||||
|
"BN254",
|
||||||
|
"IDASigners"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"/Users/wangfan/Project/0g-geth/core/vm/precompiles/interfaces/contracts/IWrappedA0GIBase.sol": {
|
||||||
|
"lastModificationDate": 1743006728499,
|
||||||
|
"contentHash": "1d44f536ff2a527d2afe8a4ef85adc7b",
|
||||||
|
"sourceName": "contracts/IWrappedA0GIBase.sol",
|
||||||
|
"solcConfig": {
|
||||||
|
"version": "0.8.20",
|
||||||
|
"settings": {
|
||||||
|
"evmVersion": "istanbul",
|
||||||
|
"optimizer": {
|
||||||
|
"enabled": true,
|
||||||
|
"runs": 200
|
||||||
|
},
|
||||||
|
"outputSelection": {
|
||||||
|
"*": {
|
||||||
|
"*": [
|
||||||
|
"abi",
|
||||||
|
"evm.bytecode",
|
||||||
|
"evm.deployedBytecode",
|
||||||
|
"evm.methodIdentifiers",
|
||||||
|
"metadata"
|
||||||
|
],
|
||||||
|
"": [
|
||||||
|
"ast"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"imports": [],
|
||||||
|
"versionPragmas": [
|
||||||
|
">=0.8.0"
|
||||||
|
],
|
||||||
|
"artifacts": [
|
||||||
|
"IWrappedA0GIBase"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
90
core/vm/precompiles/interfaces/contracts/IDASigners.sol
Normal file
90
core/vm/precompiles/interfaces/contracts/IDASigners.sol
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
// 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;
|
||||||
|
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(BN254.G1Point memory _signature) external;
|
||||||
|
|
||||||
|
function makeEpoch() external;
|
||||||
|
|
||||||
|
function getAggPkG1(
|
||||||
|
uint _epoch,
|
||||||
|
uint _quorumId,
|
||||||
|
bytes memory _quorumBitmap
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (BN254.G1Point memory aggPkG1, uint total, uint hit);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
33
core/vm/precompiles/interfaces/hardhat.config.ts
Normal file
33
core/vm/precompiles/interfaces/hardhat.config.ts
Normal 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;
|
||||||
27
core/vm/precompiles/interfaces/package.json
Normal file
27
core/vm/precompiles/interfaces/package.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
3152
core/vm/precompiles/interfaces/yarn.lock
Normal file
3152
core/vm/precompiles/interfaces/yarn.lock
Normal file
File diff suppressed because it is too large
Load diff
46
core/vm/statedb_utils.go
Normal file
46
core/vm/statedb_utils.go
Normal 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
|
||||||
|
}
|
||||||
2
go.mod
2
go.mod
|
|
@ -140,6 +140,8 @@ require (
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||||
github.com/tklauser/numcpus v0.6.1 // 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
|
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
|
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
|
||||||
golang.org/x/mod v0.22.0 // indirect
|
golang.org/x/mod v0.22.0 // indirect
|
||||||
|
|
|
||||||
4
go.sum
4
go.sum
|
|
@ -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/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.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
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 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
|
||||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
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=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ func fuzz(id byte, data []byte) int {
|
||||||
}
|
}
|
||||||
cpy := make([]byte, len(data))
|
cpy := make([]byte, len(data))
|
||||||
copy(cpy, data)
|
copy(cpy, data)
|
||||||
_, err := precompile.Run(cpy)
|
_, err := precompile.Run(nil, &vm.Contract{Input: cpy}, true)
|
||||||
if !bytes.Equal(cpy, data) {
|
if !bytes.Equal(cpy, data) {
|
||||||
panic(fmt.Sprintf("input data modified, precompile %d: %x %x", id, data, cpy))
|
panic(fmt.Sprintf("input data modified, precompile %d: %x %x", id, data, cpy))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue