Skip to content

Commit

Permalink
Merge branch 'master' into arbitrary-prune-point
Browse files Browse the repository at this point in the history
  • Loading branch information
PlasmaPower authored Apr 13, 2023
2 parents 2ed3220 + 86b963d commit 7ba1099
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 4 deletions.
14 changes: 13 additions & 1 deletion arbitrum/apibackend.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"time"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/arbitrum_types"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/log"
Expand Down Expand Up @@ -110,6 +111,13 @@ func (a *APIBackend) GetAPIs(filterSystem *filters.FilterSystem) []rpc.API {
Public: true,
})

apis = append(apis, rpc.API{
Namespace: "eth",
Version: "1.0",
Service: NewArbTransactionAPI(a),
Public: true,
})

apis = append(apis, rpc.API{
Namespace: "net",
Version: "1.0",
Expand Down Expand Up @@ -470,7 +478,11 @@ func (a *APIBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) even

// Transaction pool API
func (a *APIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
return a.b.EnqueueL2Message(ctx, signedTx)
return a.b.EnqueueL2Message(ctx, signedTx, nil)
}

func (a *APIBackend) SendConditionalTx(ctx context.Context, signedTx *types.Transaction, options *arbitrum_types.ConditionalOptions) error {
return a.b.EnqueueL2Message(ctx, signedTx, options)
}

func (a *APIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
Expand Down
3 changes: 2 additions & 1 deletion arbitrum/arbos_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package arbitrum
import (
"context"

"github.com/ethereum/go-ethereum/arbitrum_types"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
)

type ArbInterface interface {
PublishTransaction(ctx context.Context, tx *types.Transaction) error
PublishTransaction(ctx context.Context, tx *types.Transaction, options *arbitrum_types.ConditionalOptions) error
BlockChain() *core.BlockChain
ArbNode() interface{}
}
5 changes: 3 additions & 2 deletions arbitrum/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package arbitrum
import (
"context"

"github.com/ethereum/go-ethereum/arbitrum_types"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/types"
Expand Down Expand Up @@ -66,8 +67,8 @@ func (b *Backend) ChainDb() ethdb.Database {
return b.chainDb
}

func (b *Backend) EnqueueL2Message(ctx context.Context, tx *types.Transaction) error {
return b.arb.PublishTransaction(ctx, tx)
func (b *Backend) EnqueueL2Message(ctx context.Context, tx *types.Transaction, options *arbitrum_types.ConditionalOptions) error {
return b.arb.PublishTransaction(ctx, tx, options)
}

func (b *Backend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
Expand Down
68 changes: 68 additions & 0 deletions arbitrum/conditionaltx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package arbitrum

import (
"context"
"errors"

"github.com/ethereum/go-ethereum/arbitrum_types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
)

type ArbTransactionAPI struct {
b *APIBackend
}

func NewArbTransactionAPI(b *APIBackend) *ArbTransactionAPI {
return &ArbTransactionAPI{b}
}

func (s *ArbTransactionAPI) SendRawTransactionConditional(ctx context.Context, input hexutil.Bytes, options *arbitrum_types.ConditionalOptions) (common.Hash, error) {
tx := new(types.Transaction)
if err := tx.UnmarshalBinary(input); err != nil {
return common.Hash{}, err
}
return SubmitConditionalTransaction(ctx, s.b, tx, options)
}

func SubmitConditionalTransaction(ctx context.Context, b *APIBackend, tx *types.Transaction, options *arbitrum_types.ConditionalOptions) (common.Hash, error) {
// If the transaction fee cap is already specified, ensure the
// fee of the given transaction is _reasonable_.
if err := ethapi.CheckTxFee(tx.GasPrice(), tx.Gas(), b.RPCTxFeeCap()); err != nil {
return common.Hash{}, err
}
if !b.UnprotectedAllowed() && !tx.Protected() {
// Ensure only eip155 signed transactions are submitted if EIP155Required is set.
return common.Hash{}, errors.New("only replay-protected (EIP-155) transactions allowed over RPC")
}
if err := b.SendConditionalTx(ctx, tx, options); err != nil {
return common.Hash{}, err
}
// Print a log with full tx details for manual investigations and interventions
signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number())
from, err := types.Sender(signer, tx)
if err != nil {
return common.Hash{}, err
}

if tx.To() == nil {
addr := crypto.CreateAddress(from, tx.Nonce())
log.Info("Submitted contract creation", "hash", tx.Hash().Hex(), "from", from, "nonce", tx.Nonce(), "contract", addr.Hex(), "value", tx.Value())
} else {
log.Info("Submitted transaction", "hash", tx.Hash().Hex(), "from", from, "nonce", tx.Nonce(), "recipient", tx.To(), "value", tx.Value())
}
return tx.Hash(), nil
}

func SendConditionalTransactionRPC(ctx context.Context, rpc *rpc.Client, tx *types.Transaction, options *arbitrum_types.ConditionalOptions) error {
data, err := tx.MarshalBinary()
if err != nil {
return err
}
return rpc.CallContext(ctx, nil, "eth_sendRawTransactionConditional", hexutil.Encode(data), options)
}
111 changes: 111 additions & 0 deletions arbitrum_types/txoptions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package arbitrum_types

import (
"bytes"
"encoding/json"
"strings"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/rpc"
"github.com/pkg/errors"
)

type rejectedError struct {
msg string
}

func NewRejectedError(msg string) *rejectedError {
return &rejectedError{msg: msg}
}
func (e rejectedError) Error() string { return e.msg }
func (rejectedError) ErrorCode() int { return -32003 }

type limitExceededError struct {
msg string
}

func NewLimitExceededError(msg string) *limitExceededError {
return &limitExceededError{msg: msg}
}
func (e limitExceededError) Error() string { return e.msg }
func (limitExceededError) ErrorCode() int { return -32005 }

func WrapOptionsCheckError(err error, msg string) error {
wrappedMsg := func(e rpc.Error, msg string) string {
return strings.Join([]string{msg, e.Error()}, ":")
}
switch e := err.(type) {
case *rejectedError:
return NewRejectedError(wrappedMsg(e, msg))
case *limitExceededError:
return NewLimitExceededError(wrappedMsg(e, msg))
default:
return errors.Wrap(err, msg)
}
}

type RootHashOrSlots struct {
RootHash *common.Hash
SlotValue map[common.Hash]common.Hash
}

func (r *RootHashOrSlots) UnmarshalJSON(data []byte) error {
var hash common.Hash
var err error
if err = json.Unmarshal(data, &hash); err == nil {
r.RootHash = &hash
return nil
}
return json.Unmarshal(data, &r.SlotValue)
}

func (r RootHashOrSlots) MarshalJSON() ([]byte, error) {
if r.RootHash != nil {
return json.Marshal(*r.RootHash)
}
return json.Marshal(r.SlotValue)
}

type ConditionalOptions struct {
KnownAccounts map[common.Address]RootHashOrSlots `json:"knownAccounts"`
BlockNumberMin *hexutil.Uint64 `json:"blockNumberMin,omitempty"`
BlockNumberMax *hexutil.Uint64 `json:"blockNumberMax,omitempty"`
TimestampMin *hexutil.Uint64 `json:"timestampMin,omitempty"`
TimestampMax *hexutil.Uint64 `json:"timestampMax,omitempty"`
}

func (o *ConditionalOptions) Check(l1BlockNumber uint64, l2Timestamp uint64, statedb *state.StateDB) error {
if o.BlockNumberMin != nil && l1BlockNumber < uint64(*o.BlockNumberMin) {
return NewRejectedError("BlockNumberMin condition not met")
}
if o.BlockNumberMax != nil && l1BlockNumber > uint64(*o.BlockNumberMax) {
return NewRejectedError("BlockNumberMax condition not met")
}
if o.TimestampMin != nil && l2Timestamp < uint64(*o.TimestampMin) {
return NewRejectedError("TimestampMin condition not met")
}
if o.TimestampMax != nil && l2Timestamp > uint64(*o.TimestampMax) {
return NewRejectedError("TimestampMax condition not met")
}
for address, rootHashOrSlots := range o.KnownAccounts {
if rootHashOrSlots.RootHash != nil {
trie := statedb.StorageTrie(address)
if trie == nil {
return NewRejectedError("Storage trie not found for address key in knownAccounts option")
}
if trie.Hash() != *rootHashOrSlots.RootHash {
return NewRejectedError("Storage root hash condition not met")
}
} else if len(rootHashOrSlots.SlotValue) > 0 {
for slot, value := range rootHashOrSlots.SlotValue {
stored := statedb.GetState(address, slot)
if !bytes.Equal(stored.Bytes(), value.Bytes()) {
return NewRejectedError("Storage slot value condition not met")
}
}
} // else rootHashOrSlots.SlotValue is empty - ignore it and check the rest of conditions
}
return nil
}
4 changes: 4 additions & 0 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2225,6 +2225,10 @@ func (s *NetAPI) Version() string {
return fmt.Sprintf("%d", s.networkVersion)
}

func CheckTxFee(gasPrice *big.Int, gas uint64, cap float64) error {
return checkTxFee(gasPrice, gas, cap)
}

// checkTxFee is an internal function used to check whether the fee of
// the given transaction is _reasonable_(under the cap).
func checkTxFee(gasPrice *big.Int, gas uint64, cap float64) error {
Expand Down

0 comments on commit 7ba1099

Please sign in to comment.