Skip to content

Commit

Permalink
use dynamic gas for address precompile
Browse files Browse the repository at this point in the history
  • Loading branch information
Xiaoyu Chen authored and codchen committed Nov 25, 2024
1 parent 3807a81 commit d78908d
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 36 deletions.
75 changes: 41 additions & 34 deletions precompiles/addr/addr.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ type PrecompileExecutor struct {
AssociatePubKeyID []byte
}

func NewPrecompile(evmKeeper pcommon.EVMKeeper, bankKeeper pcommon.BankKeeper, accountKeeper pcommon.AccountKeeper) (*pcommon.Precompile, error) {
func NewPrecompile(evmKeeper pcommon.EVMKeeper, bankKeeper pcommon.BankKeeper, accountKeeper pcommon.AccountKeeper) (*pcommon.DynamicGasPrecompile, error) {

newAbi := pcommon.MustGetABI(f, "abi.json")

Expand All @@ -77,7 +77,7 @@ func NewPrecompile(evmKeeper pcommon.EVMKeeper, bankKeeper pcommon.BankKeeper, a
}
}

return pcommon.NewPrecompile(newAbi, p, common.HexToAddress(AddrAddress), "addr"), nil
return pcommon.NewDynamicGasPrecompile(newAbi, p, common.HexToAddress(AddrAddress), "addr"), nil
}

// RequiredGas returns the required bare minimum gas to execute the precompile.
Expand All @@ -88,72 +88,74 @@ func (p PrecompileExecutor) RequiredGas(input []byte, method *abi.Method) uint64
return pcommon.DefaultGasCost(input, p.IsTransaction(method.Name))
}

func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, _ common.Address, _ common.Address, args []interface{}, value *big.Int, readOnly bool, _ *vm.EVM) (bz []byte, err error) {
func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, _ common.Address, _ common.Address, args []interface{}, value *big.Int, readOnly bool, _ *vm.EVM, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
switch method.Name {
case GetSeiAddressMethod:
return p.getSeiAddr(ctx, method, args, value)
case GetEvmAddressMethod:
return p.getEvmAddr(ctx, method, args, value)
case Associate:
if readOnly {
return nil, errors.New("cannot call associate precompile from staticcall")
return nil, 0, errors.New("cannot call associate precompile from staticcall")
}
return p.associate(ctx, method, args, value)
case AssociatePubKey:
if readOnly {
return nil, errors.New("cannot call associate pub key precompile from staticcall")
return nil, 0, errors.New("cannot call associate pub key precompile from staticcall")
}
return p.associatePublicKey(ctx, method, args, value)
}
return
}

func (p PrecompileExecutor) getSeiAddr(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) {
func (p PrecompileExecutor) getSeiAddr(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) (ret []byte, remainingGas uint64, err error) {
if err := pcommon.ValidateNonPayable(value); err != nil {
return nil, err
return nil, 0, err

Check warning on line 113 in precompiles/addr/addr.go

View check run for this annotation

Codecov / codecov/patch

precompiles/addr/addr.go#L113

Added line #L113 was not covered by tests
}

if err := pcommon.ValidateArgsLength(args, 1); err != nil {
return nil, err
return nil, 0, err

Check warning on line 117 in precompiles/addr/addr.go

View check run for this annotation

Codecov / codecov/patch

precompiles/addr/addr.go#L117

Added line #L117 was not covered by tests
}

seiAddr, found := p.evmKeeper.GetSeiAddress(ctx, args[0].(common.Address))
if !found {
metrics.IncrementAssociationError("getSeiAddr", types.NewAssociationMissingErr(args[0].(common.Address).Hex()))
return nil, fmt.Errorf("EVM address %s is not associated", args[0].(common.Address).Hex())
return nil, 0, fmt.Errorf("EVM address %s is not associated", args[0].(common.Address).Hex())

Check warning on line 123 in precompiles/addr/addr.go

View check run for this annotation

Codecov / codecov/patch

precompiles/addr/addr.go#L123

Added line #L123 was not covered by tests
}
return method.Outputs.Pack(seiAddr.String())
ret, err = method.Outputs.Pack(seiAddr.String())
return ret, pcommon.GetRemainingGas(ctx, p.evmKeeper), err
}

func (p PrecompileExecutor) getEvmAddr(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) {
func (p PrecompileExecutor) getEvmAddr(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) (ret []byte, remainingGas uint64, err error) {
if err := pcommon.ValidateNonPayable(value); err != nil {
return nil, err
return nil, 0, err

Check warning on line 131 in precompiles/addr/addr.go

View check run for this annotation

Codecov / codecov/patch

precompiles/addr/addr.go#L131

Added line #L131 was not covered by tests
}

if err := pcommon.ValidateArgsLength(args, 1); err != nil {
return nil, err
return nil, 0, err

Check warning on line 135 in precompiles/addr/addr.go

View check run for this annotation

Codecov / codecov/patch

precompiles/addr/addr.go#L135

Added line #L135 was not covered by tests
}

seiAddr, err := sdk.AccAddressFromBech32(args[0].(string))
if err != nil {
return nil, err
return nil, 0, err

Check warning on line 140 in precompiles/addr/addr.go

View check run for this annotation

Codecov / codecov/patch

precompiles/addr/addr.go#L140

Added line #L140 was not covered by tests
}

evmAddr, found := p.evmKeeper.GetEVMAddress(ctx, seiAddr)
if !found {
metrics.IncrementAssociationError("getEvmAddr", types.NewAssociationMissingErr(args[0].(string)))
return nil, fmt.Errorf("sei address %s is not associated", args[0].(string))
return nil, 0, fmt.Errorf("sei address %s is not associated", args[0].(string))

Check warning on line 146 in precompiles/addr/addr.go

View check run for this annotation

Codecov / codecov/patch

precompiles/addr/addr.go#L146

Added line #L146 was not covered by tests
}
return method.Outputs.Pack(evmAddr)
ret, err = method.Outputs.Pack(evmAddr)
return ret, pcommon.GetRemainingGas(ctx, p.evmKeeper), err
}

func (p PrecompileExecutor) associate(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) {
func (p PrecompileExecutor) associate(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) (ret []byte, remainingGas uint64, err error) {
if err := pcommon.ValidateNonPayable(value); err != nil {
return nil, err
return nil, 0, err
}

if err := pcommon.ValidateArgsLength(args, 4); err != nil {
return nil, err
return nil, 0, err

Check warning on line 158 in precompiles/addr/addr.go

View check run for this annotation

Codecov / codecov/patch

precompiles/addr/addr.go#L158

Added line #L158 was not covered by tests
}

// v, r and s are components of a signature over the customMessage sent.
Expand All @@ -165,15 +167,15 @@ func (p PrecompileExecutor) associate(ctx sdk.Context, method *abi.Method, args

rBytes, err := decodeHexString(r)
if err != nil {
return nil, err
return nil, 0, err

Check warning on line 170 in precompiles/addr/addr.go

View check run for this annotation

Codecov / codecov/patch

precompiles/addr/addr.go#L170

Added line #L170 was not covered by tests
}
sBytes, err := decodeHexString(s)
if err != nil {
return nil, err
return nil, 0, err

Check warning on line 174 in precompiles/addr/addr.go

View check run for this annotation

Codecov / codecov/patch

precompiles/addr/addr.go#L174

Added line #L174 was not covered by tests
}
vBytes, err := decodeHexString(v)
if err != nil {
return nil, err
return nil, 0, err
}

vBig := new(big.Int).SetBytes(vBytes)
Expand All @@ -186,61 +188,62 @@ func (p PrecompileExecutor) associate(ctx sdk.Context, method *abi.Method, args
customMessageHash := crypto.Keccak256Hash([]byte(customMessage))
evmAddr, seiAddr, pubkey, err := helpers.GetAddresses(vBig, rBig, sBig, customMessageHash)
if err != nil {
return nil, err
return nil, 0, err

Check warning on line 191 in precompiles/addr/addr.go

View check run for this annotation

Codecov / codecov/patch

precompiles/addr/addr.go#L191

Added line #L191 was not covered by tests
}

return p.associateAddresses(ctx, method, evmAddr, seiAddr, pubkey)
}

func (p PrecompileExecutor) associatePublicKey(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) {
func (p PrecompileExecutor) associatePublicKey(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) (ret []byte, remainingGas uint64, err error) {
if err := pcommon.ValidateNonPayable(value); err != nil {
return nil, err
return nil, 0, err
}

if err := pcommon.ValidateArgsLength(args, 1); err != nil {
return nil, err
return nil, 0, err

Check warning on line 203 in precompiles/addr/addr.go

View check run for this annotation

Codecov / codecov/patch

precompiles/addr/addr.go#L203

Added line #L203 was not covered by tests
}

// Takes a single argument, a compressed pubkey in hex format, excluding the '0x'
pubKeyHex := args[0].(string)

pubKeyBytes, err := hex.DecodeString(pubKeyHex)
if err != nil {
return nil, err
return nil, 0, err
}

// Parse the compressed public key
pubKey, err := btcec.ParsePubKey(pubKeyBytes, btcec.S256())
if err != nil {
return nil, err
return nil, 0, err

Check warning on line 217 in precompiles/addr/addr.go

View check run for this annotation

Codecov / codecov/patch

precompiles/addr/addr.go#L217

Added line #L217 was not covered by tests
}

// Convert to uncompressed public key
uncompressedPubKey := pubKey.SerializeUncompressed()

evmAddr, seiAddr, pubkey, err := helpers.GetAddressesFromPubkeyBytes(uncompressedPubKey)
if err != nil {
return nil, err
return nil, 0, err

Check warning on line 225 in precompiles/addr/addr.go

View check run for this annotation

Codecov / codecov/patch

precompiles/addr/addr.go#L225

Added line #L225 was not covered by tests
}

return p.associateAddresses(ctx, method, evmAddr, seiAddr, pubkey)
}

func (p PrecompileExecutor) associateAddresses(ctx sdk.Context, method *abi.Method, evmAddr common.Address, seiAddr sdk.AccAddress, pubkey cryptotypes.PubKey) ([]byte, error) {
func (p PrecompileExecutor) associateAddresses(ctx sdk.Context, method *abi.Method, evmAddr common.Address, seiAddr sdk.AccAddress, pubkey cryptotypes.PubKey) (ret []byte, remainingGas uint64, err error) {
// Check that address is not already associated
_, found := p.evmKeeper.GetEVMAddress(ctx, seiAddr)
if found {
return nil, fmt.Errorf("address %s is already associated with evm address %s", seiAddr, evmAddr)
return nil, 0, fmt.Errorf("address %s is already associated with evm address %s", seiAddr, evmAddr)
}

// Associate Addresses:
associationHelper := helpers.NewAssociationHelper(p.evmKeeper, p.bankKeeper, p.accountKeeper)
err := associationHelper.AssociateAddresses(ctx, seiAddr, evmAddr, pubkey)
err = associationHelper.AssociateAddresses(ctx, seiAddr, evmAddr, pubkey)
if err != nil {
return nil, err
return nil, 0, err

Check warning on line 242 in precompiles/addr/addr.go

View check run for this annotation

Codecov / codecov/patch

precompiles/addr/addr.go#L242

Added line #L242 was not covered by tests
}

return method.Outputs.Pack(seiAddr.String(), evmAddr)
ret, err = method.Outputs.Pack(seiAddr.String(), evmAddr)
return ret, pcommon.GetRemainingGas(ctx, p.evmKeeper), err
}

func (PrecompileExecutor) IsTransaction(method string) bool {
Expand All @@ -252,6 +255,10 @@ func (PrecompileExecutor) IsTransaction(method string) bool {
}
}

func (p PrecompileExecutor) EVMKeeper() pcommon.EVMKeeper {
return p.evmKeeper
}

func decodeHexString(hexString string) ([]byte, error) {
trimmed := strings.TrimPrefix(hexString, "0x")
if len(trimmed)%2 != 0 {
Expand Down
41 changes: 39 additions & 2 deletions precompiles/addr/addr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func TestAssociatePubKey(t *testing.T) {
require.Nil(t, err)

// Make the call to associate.
ret, err := p.Run(tt.args.evm, tt.args.caller, tt.args.caller, append(p.GetExecutor().(*addr.PrecompileExecutor).AssociatePubKeyID, inputs...), tt.args.value, tt.args.readOnly, false)
ret, _, err := p.RunAndCalculateGas(tt.args.evm, tt.args.caller, tt.args.caller, append(p.GetExecutor().(*addr.PrecompileExecutor).AssociatePubKeyID, inputs...), 40000, tt.args.value, nil, tt.args.readOnly, false)
if (err != nil) != tt.wantErr {
t.Errorf("Run() error = %v, wantErr %v %v", err, tt.wantErr, string(ret))
return
Expand All @@ -166,6 +166,7 @@ func TestAssociate(t *testing.T) {

pre, _ := addr.NewPrecompile(k, k.BankKeeper(), k.AccountKeeper())
associate, err := pre.ABI.MethodById(pre.GetExecutor().(*addr.PrecompileExecutor).AssociateID)
require.Nil(t, err)

// Target refers to the address that the caller is trying to associate.
targetPrivKey := testkeeper.MockPrivateKey()
Expand Down Expand Up @@ -331,7 +332,7 @@ func TestAssociate(t *testing.T) {
require.Nil(t, err)

// Make the call to associate.
ret, err := p.Run(tt.args.evm, tt.args.caller, tt.args.caller, append(p.GetExecutor().(*addr.PrecompileExecutor).AssociateID, inputs...), tt.args.value, tt.args.readOnly, false)
ret, _, err := p.RunAndCalculateGas(tt.args.evm, tt.args.caller, tt.args.caller, append(p.GetExecutor().(*addr.PrecompileExecutor).AssociateID, inputs...), 40000, tt.args.value, nil, tt.args.readOnly, false)
if (err != nil) != tt.wantErr {
t.Errorf("Run() error = %v, wantErr %v %v", err, tt.wantErr, string(ret))
return
Expand All @@ -348,3 +349,39 @@ func TestAssociate(t *testing.T) {
})
}
}

func TestGetAddr(t *testing.T) {
testApp := testkeeper.EVMTestApp
ctx := testApp.NewContext(false, tmtypes.Header{}).WithBlockHeight(2)
k := &testApp.EvmKeeper

pre, _ := addr.NewPrecompile(k, k.BankKeeper(), k.AccountKeeper())
getSeiAddr, err := pre.ABI.MethodById(pre.GetExecutor().(*addr.PrecompileExecutor).GetSeiAddressID)
require.Nil(t, err)
getEvmAddr, err := pre.ABI.MethodById(pre.GetExecutor().(*addr.PrecompileExecutor).GetEvmAddressID)
require.Nil(t, err)

seiAddr, evmAddr := testkeeper.MockAddressPair()
k.SetAddressMapping(ctx, seiAddr, evmAddr)

stateDB := &state.DBImpl{}
stateDB.WithCtx(ctx)

getSeiAddrBz, err := getSeiAddr.Inputs.Pack(evmAddr)
require.Nil(t, err)
res, _, err := pre.RunAndCalculateGas(&vm.EVM{StateDB: stateDB}, evmAddr, evmAddr, append(getSeiAddr.ID, getSeiAddrBz...), 20000, common.Big0, nil, true, false)
require.Nil(t, err)
unpacked, err := getSeiAddr.Outputs.Unpack(res)
require.Nil(t, err)
require.Equal(t, 1, len(unpacked))
require.Equal(t, seiAddr.String(), unpacked[0].(string))

getEvmAddrBz, err := getEvmAddr.Inputs.Pack(seiAddr.String())
require.Nil(t, err)
res, _, err = pre.RunAndCalculateGas(&vm.EVM{StateDB: stateDB}, evmAddr, evmAddr, append(getEvmAddr.ID, getEvmAddrBz...), 20000, common.Big0, nil, true, false)
require.Nil(t, err)
unpacked, err = getEvmAddr.Outputs.Unpack(res)
require.Nil(t, err)
require.Equal(t, 1, len(unpacked))
require.Equal(t, evmAddr, unpacked[0].(common.Address))
}
1 change: 1 addition & 0 deletions precompiles/common/precompiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ func HandlePrecompileError(err error, evm *vm.EVM, operation string) {
}

func (p Precompile) Prepare(evm *vm.EVM, input []byte) (sdk.Context, *abi.Method, []interface{}, error) {
fmt.Println(1)
ctxer, ok := evm.StateDB.(Contexter)
if !ok {
return sdk.Context{}, nil, nil, errors.New("cannot get context from EVM")
Expand Down

0 comments on commit d78908d

Please sign in to comment.