ethermint icon indicating copy to clipboard operation
ethermint copied to clipboard

Support stateful precompiled contract

Open yihuang opened this issue 3 years ago • 6 comments

Proposal: Support stateful precompiled contract

Current behavior:

EVM support registering precompiled smart contract, but we can't simply modify the state in it, pitfalls of doing that, basically it doesn't play well with the statedb's snapshot and revert.

Desired behavior: Make stateful precompiled smart contract possible.

Use case:

Precompiled smart contract is a powerful tool for many use cases.

Allow precompile to extend statedb in memory logic somehow.

yihuang avatar Jun 06 '22 15:06 yihuang

type StatefulPrecompiled interface {
  PrecompiledContract
  Commit(ctx sdk.Context) error
}

type ExtStateDB interface {
  StateDB
  AppendJournalEntry(entry journalEntry)
}

type StateDB struct {
  precompiles map[Address]StatefulPrecompiled
}

func (db *StateDB) Commit(ctx sdk.Context) error {
  for _, contract := range db.precompiles {
    if err := contract.Commit(ctx); err != nil {
      return err
    }
  }
}

db := NewStateDB()
db.precompiles := {
  addr: NewExampleContract(ctx, db),
}
evm := NewEVM(db, db.precompiles)

evm.Call(...)
type ExampleContract struct {
  db StateDB
  state ExampleState
}

func NewExampleContract(ctx sdk.Context, db StateDB) *ExampleContract {
  return &ExampleContract {
    db: db,
    state: loadState(ctx),
  }
}

func (p *ExampleContract) RequiredGas(input []byte) uint64 {
  // TODO
}

func (p *ExampleContract) Run(input []byte) ([]byte, error) {
  p.db.append(revertEntry{
    p: p,
    saved: p.state,
  })
  // modify p.state in memory
}

func (p *ExampleContract) Commit(ctx sdk.Context) error {
  // write p.state into cosmos-sdk storage.
}

// implements journalEntry interface
type revertEntry struct {
  p *ExampleContract
  saved ExampleState
}

func (r revertEntry) revert(*StateDB) {
  r.p.state = r.saved
}

func (r revertEntry) dirtied() *common.Address {
  // TODO
}

yihuang avatar Jun 06 '22 16:06 yihuang

An example bank transfer precompiled contract:

type BankTransferContract struct {
  bankKeeper BankKeeper
  cachedCtx sdk.Context
  writeCache CacheWriter
  stateDB    ExtStateDB
}

func NewBankTransferContract(ctx sdk.Context, bankKeeper BankKeeer, stateDB StateDB) *BankTransferContract {
  cachedCtx, writeCache := ctx.CacheContext()
  return &BankTransferContract{
    bankKeeper, cachedCtx, writeCache, stateDB,
  }
}

func (bc *BankTransferContract) Run(input []byte) error {
  from, to, amount := decodeInput(input);
  if err := bc.bankKeeper.Transfer(bc.cachedCtx, from, to, amount); err != nil {
    return err
  }
  bc.stateDB.AppendJournalEntry(revertBankEntry{
    bc.bankKeeper, bc.cachedCtx, from, to, amount,
  })
}

func (bc *BankContract) Commit(ctx sdk.Context) error {
  bc.writeCache()
}

type revertBankEntry struct {
  bankKeeper BankKeeper
  ctx sdk.Context
  from, to, amount
}

func (r revertBankEntry) revert(*StateDB) {
  if err := r.bankKeeper.Transfer(r.ctx, to, from, amount); err != nil {
    // invariant, should not fail.
  }
}

func (r revertBankEntry) dirtied() *common.Address {
  return nil
}

yihuang avatar Jun 07 '22 01:06 yihuang

should look into making special stripped out go-ethereum fork specifically for ethermint.

(Think SubnetEVM)

itsdevbear avatar Jun 24 '22 15:06 itsdevbear

(Maybe that's even the place to start)

itsdevbear avatar Jun 24 '22 15:06 itsdevbear

This issue is stale because it has been open 45 days with no activity. Remove Status: Stale label or comment or this will be closed in 7 days.

github-actions[bot] avatar Aug 09 '22 02:08 github-actions[bot]

unstale

yihuang avatar Aug 09 '22 02:08 yihuang

This issue is stale because it has been open 45 days with no activity. Remove Status: Stale label or comment or this will be closed in 7 days.

github-actions[bot] avatar Sep 25 '22 00:09 github-actions[bot]