Skip to content
This repository has been archived by the owner on Dec 5, 2021. It is now read-only.

Offchain prototype WIP - do not merge #350

Open
wants to merge 26 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9ccc057
Squashed commit to move over the "mmontour1306/offchain-prototype" code
mmontour1306 Aug 4, 2021
7244c29
Use StaticJsonRpcProvider to reduce the number of chainId lookups.
mmontour1306 Aug 4, 2021
ebebf4f
Adjust various intervals to reduce log volume during development.
mmontour1306 Aug 4, 2021
c18455e
Clean up packages.json and change some versions to match what's
mmontour1306 Aug 4, 2021
6cc6411
Merge branch 'develop' into offchain-prototype
mmontour1306 Aug 5, 2021
93c8967
Change the dependencies so that l2geth depends on dtl rather than
mmontour1306 Aug 5, 2021
4287fc1
Add a very simple cache of the last off-chain result, so that
mmontour1306 Aug 5, 2021
e553ea3
Merge branch 'develop' into offchain-prototype
mmontour1306 Aug 6, 2021
62fe346
create hardhat_swap folder with example stableSwap smart contract wit…
Aug 6, 2021
80f5a5f
Merge branch 'develop' into exampleSwap
CAPtheorem Aug 6, 2021
62d1b27
Merge branch 'develop' into exampleSwap
CAPtheorem Aug 11, 2021
36a8f1a
Merge branch 'develop' into offchain-prototype
mmontour1306 Aug 11, 2021
62a449b
Merge branch 'exampleSwap' into offchain-prototype
mmontour1306 Aug 11, 2021
8e3c6c4
Generalize the RLP encoding to allow arbitrary abiEncoded payloads. R…
mmontour1306 Aug 12, 2021
899a2b1
Remove the now-broken mock L1 mode.
mmontour1306 Aug 16, 2021
a8d98f6
Merge branch 'develop' into offchain-prototype
mmontour1306 Aug 16, 2021
f391f1f
Merge branch 'develop' into offchain-prototype
mmontour1306 Aug 24, 2021
9328880
fix fee-test.ts and add sqrt off-chain for stable-test.ts (#330)
agarwalml Sep 6, 2021
5199db8
add AWS endpoint toy examples (#318)
CAPtheorem Sep 6, 2021
b9c2dfe
TURING OFFCHAIN COMPUTE: add uniswapv2 fee example (incomplete) - WIP…
agarwalml Sep 6, 2021
86ae989
TURING OFFCHAIN COMPUTE: Offchain prototype mult classifier (#317)
jemeza Sep 6, 2021
4648aed
Update yarn.lock
CAPtheorem Sep 6, 2021
1f0c74b
Merge branch 'develop' into offchain-prototype
CAPtheorem Sep 6, 2021
a794a73
Update yarn.lock
CAPtheorem Sep 6, 2021
43c7a88
Merge branch 'develop' into offchain-prototype
mmontour1306 Sep 19, 2021
0908121
Update TuringHelper to log the offchain response when called in a
mmontour1306 Sep 19, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ env-mainnet.yml
*.log

.serverless
__pycache__

# subgraph
build
Expand Down
11 changes: 11 additions & 0 deletions l2geth/core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"

// "bytes"
// "github.com/ethereum/go-ethereum/log"
// "github.com/ethereum/go-ethereum/common/hexutil"
)

// StateProcessor is a basic Processor, which takes care of transitioning
Expand Down Expand Up @@ -83,6 +87,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) {
var msg Message
var err error
//log.Debug("MMDBG state_processor.go entering ApplyTransaction")

if !vm.UsingOVM {
msg, err = tx.AsMessage(types.MakeSigner(config, header.Number))
if err != nil {
Expand Down Expand Up @@ -115,6 +121,10 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
vmenv := vm.NewEVM(context, statedb, config, cfg)
// Apply the transaction to the current state (included in the env)
_, gas, failed, err := ApplyMessage(vmenv, msg, gp)
//result, gas, failed, err := ApplyMessage(vmenv, msg, gp)

//log.Debug("MMDBG state_processor.go ApplyMessage result", "failed", failed, "err", err, "gas", gas, "result", hexutil.Bytes(result), "turing", bytes.Contains(result, []byte("_OMGXTURING_")))

if err != nil {
return nil, err
}
Expand Down Expand Up @@ -142,6 +152,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
receipt.BlockHash = statedb.BlockHash()
receipt.BlockNumber = header.Number
receipt.TransactionIndex = uint(statedb.TxIndex())
//log.Debug("MMDBG state_processor.go leaving ApplyTransaction", "receipt", receipt)

return receipt, err
}
2 changes: 2 additions & 0 deletions l2geth/core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,9 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
st.state.SetNonce(msg.From(), st.state.GetNonce(msg.From())+1)
}

//log.Debug("MMDBG state_transition.go before evm.Call", "st.data", hexutil.Bytes(st.data))
ret, st.gas, vmerr = evm.Call(sender, st.to(), st.data, st.gas, st.value)
//log.Debug("MMDBG state_transition.go evm.Call result","ret", hexutil.Bytes(ret), "st.gas", st.gas, "vmerr", vmerr)
}

if vmerr != nil {
Expand Down
179 changes: 179 additions & 0 deletions l2geth/core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"strings"
"sync/atomic"
"time"
"sync"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
Expand All @@ -31,6 +32,9 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rollup/dump"

"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
)

// codec is a decoder for the return values of the execution manager. It decodes
Expand Down Expand Up @@ -160,6 +164,10 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
}(evm.interpreter)
evm.interpreter = interpreter
}
if !bytes.HasPrefix((contract.Address()).Bytes(), deadPrefix) {
log.Debug("MMDBG processing contract", "Address", contract.Address().Hex())
}

return interpreter.Run(contract, input, readOnly)
}
}
Expand Down Expand Up @@ -201,6 +209,26 @@ type Context struct {
OvmL2StandardBridge dump.OvmDumpAccount
}

// FIXME - should move this somewhere else.
// For now, only caches the most recent result. Can be extended with a map of
// multiple requests, but that needs some logic to expire/purge old entries.
// "key" for now is simply the request URL. May need tighter scope in the future,
// e.g. per contract. That would also allow different expiration thresholds for
// different users.
//
// Another future enhancement could be to allow an external program to pre-load
// results into the cache on a periodic basis (e.g. updating the latest market
// prices for various tokens). Contracts would then be able to access this data
// without the latency of making an off-chain JSON-RPC call. This is similar to
// some of the earlier concepts for a "Turing" mechanism.

var turingCache struct {
lock sync.RWMutex
expires time.Time
key string
value []byte
}

// EVM is the Ethereum Virtual Machine base object and provides
// the necessary tools to run a contract on the given state with
// the provided context. It should be noted that any error
Expand Down Expand Up @@ -301,11 +329,133 @@ func (evm *EVM) Interpreter() Interpreter {
return evm.interpreter
}

// In response to an off-chain Turing request, obtain the requested data and
// rewrite the parameters so that the contract can be called a second time.
// FIXME - needs error handling. For now, bails out and lets the contract
// be called a second time with the original parameters. 2nd failure is not intercepted.

func omgxTuringCall(reqString []byte, oldValue hexutil.Bytes) hexutil.Bytes {
var responseStringEnc string
var responseString []byte
var reqFields [4]string

prefix := make([]byte, 4)
copy(prefix,oldValue[0:4])

// If decoding fails, we'll return a "0" parameter which should fail a
// "require" in the contract without generating another OMGX_TURING marker.
// FIXME - would be cleaner to return nil here and put better error handling
// into l2geth to avoid that second call into the contract.

// Some other consistency checks. Probably OK to remove these at some point.
rest := oldValue[4:]
rlen := len(rest)
method_idx := rest[0:32] // This value is preserved and passed back on the 2nd call
bad := append(prefix, method_idx...)
bad = append(bad, hexutil.MustDecode(fmt.Sprintf("0x%064x", 0))...)

log.Debug ("MMDBG decode oldValue", "prefix", prefix, "rest", rest, "rlen", rlen)

if (rlen < 128) {
log.Warn("MMDBG Unexpected oldValue in omgxTuringCall", "len < 128", rlen)
return bad
}

rType_big := new(big.Int).SetBytes(rest[32:64]) // 1 for Request, 2 for Response
rType := int(rType_big.Uint64())
if (rType != 1) {
log.Warn("MMDBG unexpected oldValue in omgxTuringCall", "rType", rType)
return bad
}

if err := rlp.Decode(bytes.NewReader(reqString), &reqFields); err != nil {
log.Warn("MMDBG RLP decoding failed", "err", err)
return bad
} else {
log.Debug("MMDBG RLP decoded OK", "reqFields", reqFields)
}

reqVer := reqFields[0]

if reqVer != "\x01" {
log.Warn("MMDBG Unexpected request version", "ver", hexutil.Bytes(reqVer))
return bad
}
reqUrl := reqFields[1]
reqMethod := reqFields[2]
reqValue := reqFields[3]

var ret hexutil.Bytes

turingCache.lock.Lock()
if reqValue == turingCache.key && time.Now().Before(turingCache.expires) {
ret = turingCache.value
}
turingCache.lock.Unlock()

if ret != nil {
log.Debug("MMDBG turingCache hit for", "key", reqValue)
return ret
}

client,err := rpc.Dial(reqUrl)
if client != nil {
log.Debug("MMDBG Calling off-chain client at", "url", reqFields[1])
if err := client.Call(&responseStringEnc, reqMethod, hexutil.Bytes(reqValue)); err != nil {
log.Warn("MMDBG client error", "err", err)
return bad
}
responseString, err = hexutil.Decode(responseStringEnc)
if err != nil {
log.Warn("MMDBG error decoding responseString", "err", err)
return bad
}
} else {
log.Warn("MMDBG Failed to create client for off-chain request", "err", err)
return bad
}

log.Debug("MMDBG Generating Turing response", "Request", reqValue, "Response", responseString)

rsLen := len(responseString)

new_val := hexutil.MustDecode(fmt.Sprintf("0x%064x", rsLen))
rsBytes := []byte(responseString)
new_val = append(new_val, rsBytes...)

tmpLen := len(new_val) % 32
if tmpLen > 0 {
pad := bytes.Repeat([]byte{0}, 32 - tmpLen)
new_val = append(new_val, pad...)
}
ret = append(prefix, method_idx...)
ret = append(ret, hexutil.MustDecode(fmt.Sprintf("0x%064x", 2))...)
ret = append(ret, hexutil.MustDecode(fmt.Sprintf("0x%064x", 96))...)
ret = append(ret, new_val...)

log.Debug("MMDBG Modified parameters", "newValue", ret)

turingCache.lock.Lock()
turingCache.key = reqValue
turingCache.expires = time.Now().Add(2*time.Second)
turingCache.value = ret
turingCache.lock.Unlock()

log.Debug("MMDBG turingCache entry stored for", "key", reqValue)
return ret
}


// Call executes the contract associated with the addr with the given input as
// parameters. It also handles any necessary value transfer required and takes
// the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer.
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
//log.Debug("MMDBG entering evm Call", "input", hexutil.Bytes(input))
if !bytes.HasPrefix(addr.Bytes(), deadPrefix) {
log.Debug("MMDBG entering Call", "depth", evm.depth, "addr", addr, "input", hexutil.Bytes(input), "gas", gas);
}

if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}
Expand Down Expand Up @@ -391,6 +541,28 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas

ret, err = run(evm, contract, input, false)

if ! bytes.HasPrefix(contract.CodeAddr.Bytes(), deadPrefix) {
//log.Debug("MMDBG evm.go run", "contract", contract.CodeAddr, "ret", hexutil.Bytes(ret), "err", err)
}

if err != nil {
if ! bytes.HasPrefix(contract.CodeAddr.Bytes(), deadPrefix) {
isTuring := bytes.Contains(ret, []byte("_OMGXTURING_"))
log.Debug("MMDBG evm.go run result", "err", err, "ret", hexutil.Bytes(ret), "input", hexutil.Bytes(input), "contract", contract.CodeAddr, "turing", isTuring)

if isTuring && UsingOVM {
ii := bytes.Index(ret, []byte("_OMGXTURING_"))
rest := ret[ii+12:]

new_in := omgxTuringCall(rest, input)

//evm.StateDB.RevertToSnapshot(snapshot) // FIXME?
ret, err = run(evm, contract, new_in, false)
log.Debug("MMDBG evm.go run2 result", "err", err, "ret", hexutil.Bytes(ret), "new_in", hexutil.Bytes(new_in), "contract", contract.CodeAddr, "turing", bytes.Contains(ret, []byte("_OMGXTURING_")))
}
}
}

// If all of these very particular conditions hold then we're guaranteed to be in a successful
// L1 => L2 message. It's not pretty, but it works. Broke this out into a series of checks to
// make it a bit more legible.
Expand Down Expand Up @@ -450,6 +622,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// execution with empty returndata
_ = codec.Unpack(&inner, "call", returnData.ReturnData)
if !inner.Success {
log.Debug("MMDBG evm.go errExecutionReverted")
err = errExecutionReverted
}
ret = inner.ReturnData
Expand All @@ -462,6 +635,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
}
}
}
if !bytes.HasPrefix(addr.Bytes(), deadPrefix) {
log.Debug("MMDBG exiting Call", "depth", evm.depth, "addr", addr, "ret", hexutil.Bytes(ret), "err", err);
}

return ret, contract.Gas, err
}
Expand Down Expand Up @@ -547,6 +723,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
// Opcodes that attempt to perform such modifications will result in exceptions
// instead of performing the modifications.
func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
if !bytes.HasPrefix(addr.Bytes(), deadPrefix) {
log.Debug("MMDBG in StaticCall", "addr", addr);
}
if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}
Expand Down
1 change: 1 addition & 0 deletions l2geth/eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ func (b *EthAPIBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscri
// a lock can be used around the remotes for when the sequencer is reorganizing.
func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
if b.UsingOVM {
//log.Debug("MMDBG api_backend.go SendTx", "signedTx", signedTx)
to := signedTx.To()
if to != nil {
// Prevent QueueOriginSequencer transactions that are too large to
Expand Down
11 changes: 9 additions & 2 deletions l2geth/internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,10 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs
log.Warn("Failed transaction send attempt", "from", args.From, "to", args.To, "value", args.Value.ToInt(), "err", err)
return common.Hash{}, err
}
return SubmitTransaction(ctx, s.b, signed)
//log.Debug("MMDBG api.go entering SendTransaction")
ret, err := SubmitTransaction(ctx, s.b, signed)
//log.Debug("MMDBG api.go SendTransaction returning", "RET", ret, "ERR", err)
return ret, err
}

// SignTransaction will create a transaction from the given arguments and
Expand Down Expand Up @@ -1718,7 +1721,11 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod
// L1Timestamp and L1BlockNumber will be set right before execution
txMeta := types.NewTransactionMeta(nil, 0, nil, types.QueueOriginSequencer, nil, nil, encodedTx)
tx.SetTransactionMeta(txMeta)
return SubmitTransaction(ctx, s.b, tx)

log.Debug("MMDBG api.go entering SendRawTransaction", "TX", tx)
ret, err := SubmitTransaction(ctx, s.b, tx)
log.Debug("MMDBG api.go SendRawTransaction returning", "RET", ret, "ERR", err) // ret is (type common.Hash)
return ret, err
}

// Sign calculates an ECDSA signature for:
Expand Down
6 changes: 6 additions & 0 deletions l2geth/miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,10 @@ func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Addres
snap := w.current.state.Snapshot()

receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, *w.chain.GetVMConfig())

if err != nil || receipt.Status != 1 {
log.Warn("MMDBG worker.go ApplyTransaction result", "err", err, "receipt", receipt)
}
if err != nil {
w.current.state.RevertToSnapshot(snap)
return nil, err
Expand Down Expand Up @@ -890,6 +894,7 @@ func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coin
// on reading from a channel that is written to when a new block is added to the
// chain.
func (w *worker) commitNewTx(tx *types.Transaction) error {
//log.Debug("MMDBG worker.go entering commitNewTx")
w.mu.RLock()
defer w.mu.RUnlock()
tstart := time.Now()
Expand Down Expand Up @@ -947,6 +952,7 @@ func (w *worker) commitNewTx(tx *types.Transaction) error {
if w.commitTransactions(txs, w.coinbase, nil) {
return errors.New("Cannot commit transaction in miner")
}
//log.Debug("MMDBG worker.go leaving commitNewTx")
return w.commit(nil, w.fullTaskHook, true, tstart)
}

Expand Down
2 changes: 1 addition & 1 deletion l2geth/params/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const (
VersionMajor = 1 // Major version component of the current release
VersionMinor = 9 // Minor version component of the current release
VersionPatch = 10 // Patch version component of the current release
VersionMeta = "stable" // Version metadata to append to the version string
VersionMeta = "mm1" // Version metadata to append to the version string
)

// Version holds the textual version string.
Expand Down
Loading