Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ ARG VERSION=""
ARG BUILDNUM=""

# Build Geth in a stock Go builder container
FROM golang:1.22-alpine as builder
FROM golang:1.22-alpine AS builder

RUN apk add --no-cache gcc musl-dev linux-headers git

Expand Down
17 changes: 7 additions & 10 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -477,21 +477,18 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
)
if contractCreation {
ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, value)
} else {
// Increment the nonce for the next transaction
st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1)
ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, value)
}

// if this is a injected tx, don't refund gas and also don't pay to the coinbase,
// as no gas was used.
if st.msg.IsInjectedTx {
} else if msg.IsInjectedTx {
ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, ^uint64(0), value)
log.Debug("injected tx executed", "to", *st.msg.To, "value", st.msg.Value, "from", st.msg.From, "gasUsed", st.gasUsed(), "err", vmerr)
return &ExecutionResult{
UsedGas: st.gasUsed(),
UsedGas: 0,
Err: vmerr,
ReturnData: ret,
}, nil
} else {
// Increment the nonce for the next transaction
st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1)
ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, value)
}

var gasRefund uint64
Expand Down
8 changes: 4 additions & 4 deletions grpc/execution/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func (s *ExecutionServiceServerV2) CreateExecutionSession(ctx context.Context, r
}

// sanity code check for oracle contract address
if fork.Oracle.ContractAddress != (common.Address{}) {
if fork.Oracle.ContractAddress != nil {
height := s.bc.CurrentFinalBlock().Number.Uint64() // consider should this be the current final block, safe block, or the fork start height - 1?
state, header, err := s.eth.APIBackend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(height))
if err != nil {
Expand All @@ -136,7 +136,7 @@ func (s *ExecutionServiceServerV2) CreateExecutionSession(ctx context.Context, r
}

evm := s.eth.APIBackend.GetEVM(context.Background(), &core.Message{GasPrice: big.NewInt(0)}, state, header, &vm.Config{NoBaseFee: true}, nil)
code := evm.StateDB.GetCode(fork.Oracle.ContractAddress)
code := evm.StateDB.GetCode(*fork.Oracle.ContractAddress)
if len(code) == 0 {
log.Error("oracle contract address has no code", "address", fork.Oracle.ContractAddress)
return nil, status.Error(codes.FailedPrecondition, "Oracle contract address has no code")
Expand Down Expand Up @@ -205,8 +205,8 @@ type conversionConfig struct {
bridgeAddresses map[string]*params.AstriaBridgeAddressConfig
bridgeAllowedAssets map[string]struct{}
api *eth.EthAPIBackend
oracleContractAddress common.Address
oracleCallerAddress common.Address
oracleContractAddress *common.Address
oracleCallerAddress *common.Address
}

// ExecuteBlock drives deterministic derivation of a rollup block from sequencer
Expand Down
4 changes: 2 additions & 2 deletions grpc/execution/test_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ func (s *ExecutionServiceServerV2) createExecutionSessionWithForkOverride(ctx co
}

// sanity code check for oracle contract address
if fork.Oracle.ContractAddress != (common.Address{}) {
if fork.Oracle.ContractAddress != nil {
height := s.bc.CurrentFinalBlock().Number.Uint64() // consider should this be the current final block, safe block, or the fork start height - 1?
state, header, err := s.eth.APIBackend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(height))
if err != nil {
Expand All @@ -252,7 +252,7 @@ func (s *ExecutionServiceServerV2) createExecutionSessionWithForkOverride(ctx co
}

evm := s.eth.APIBackend.GetEVM(context.Background(), &core.Message{GasPrice: big.NewInt(0)}, state, header, &vm.Config{NoBaseFee: true}, nil)
code := evm.StateDB.GetCode(fork.Oracle.ContractAddress)
code := evm.StateDB.GetCode(*fork.Oracle.ContractAddress)
if len(code) == 0 {
log.Error("oracle contract address has no code", "address", fork.Oracle.ContractAddress)
return nil, status.Error(codes.FailedPrecondition, "Oracle contract address has no code")
Expand Down
58 changes: 34 additions & 24 deletions grpc/execution/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,13 @@ func validateAndConvertPriceFeedDataTx(
cfg *conversionConfig,
sequencerBlockHash string,
) (*AstriaTransactionsWithType, error) {
if cfg.oracleContractAddress == nil || cfg.oracleCallerAddress == nil {
return nil, fmt.Errorf("price feed data invalid when oracle contract address or caller address is not set")
}
txs := make([]*types.Transaction, 0)

log.Debug("creating price feed data update tx", "price count", len(priceFeedData.Prices))
abi, err := contracts.AstriaOracleMetaData.GetAbi()
oracleAbi, err := contracts.AstriaOracleMetaData.GetAbi()
if err != nil {
// this should never happen, as the abi is hardcoded in the contract bindings
return nil, fmt.Errorf("failed to get abi for AstriaOracle: %w", err)
Expand All @@ -63,7 +66,7 @@ func validateAndConvertPriceFeedDataTx(

// see if contract requires currency pair authorization
evm := cfg.api.GetEVM(ctx, &core.Message{GasPrice: big.NewInt(0)}, state, header, &vm.Config{NoBaseFee: true}, nil)
res, err := callContract(abi, evm, cfg.oracleCallerAddress, cfg.oracleContractAddress, "requireCurrencyPairAuthorization")
res, err := callContract(oracleAbi, evm, *cfg.oracleCallerAddress, *cfg.oracleContractAddress, "requireCurrencyPairAuthorization")
if err != nil {
return nil, fmt.Errorf("failed to call requireCurrencyPairAuthorization: %w", err)
}
Expand All @@ -78,15 +81,15 @@ func validateAndConvertPriceFeedDataTx(
return nil, fmt.Errorf("unexpected return type for requireCurrencyPairAuthorization: %T", res[0])
}

// Building a list of price pairs which will be set in a single contract call.
// Configure any currency pairs which are not initialized with current decimal value in the parent state,
// and skip any pairs which are not authorized.
for _, price := range priceFeedData.Prices {
currencyPairHash := hashCurrencyPair(price.CurrencyPair)

// see if currency pair was already initialized; if not, initialize it
//
// to check if it was initialized, we call `currencyPairInfo()` on the parent state; since oracle data is always top of block,
// to check if it was initialized, we call `currencyPairInfo()` on the parent state;
// if the currency pair is not initialized in the parent state, then we need to initialize it here
// as it has never been initialized before.
res, err := callContract(abi, evm, cfg.oracleCallerAddress, cfg.oracleContractAddress, "currencyPairInfo", currencyPairHash)
res, err := callContract(oracleAbi, evm, *cfg.oracleCallerAddress, *cfg.oracleContractAddress, "currencyPairInfo", currencyPairHash)
if err != nil {
return nil, fmt.Errorf("failed to call currencyPairInfo: %w", err)
}
Expand All @@ -95,21 +98,27 @@ func validateAndConvertPriceFeedDataTx(
if len(res) != 2 {
return nil, fmt.Errorf("unexpected result length from currencyPairInfo: %d", len(res))
}

init, ok := res[0].(bool)
if !ok {
return nil, fmt.Errorf("unexpected return type for initialized: %T", res[0])
}
if init {
decimals, ok := res[1].(uint8)
if !ok {
return nil, fmt.Errorf("unexpected return type for decimals: %T", res[1])
}

// We can skip any further action on the pair and add to prices which need to be set if already initialized
// with decimals matching the latest price feed data.
if init && uint64(decimals) == price.Decimals {
currencyPairsToSet = append(currencyPairsToSet, currencyPairHash)
pricesToSet = append(pricesToSet, protoI128ToBigInt(price.Price))
continue
}

// if contract requires currency pair authorization to initialize,
// check if pair is authorized and skip it if not
if requireCurrencyPairAuthorization {
res, err := callContract(abi, evm, cfg.oracleCallerAddress, cfg.oracleContractAddress, "authorizedCurrencyPairs", currencyPairHash)
// When `requireCurrencyPairAuthorization` is true, we want to skip unauthorized currency pairs
// any currency pair which is already initialized is implicitly authorized.
if requireCurrencyPairAuthorization && !init {
res, err := callContract(oracleAbi, evm, *cfg.oracleCallerAddress, *cfg.oracleContractAddress, "authorizedCurrencyPairs", currencyPairHash)
if err != nil {
return nil, fmt.Errorf("failed to call authorizedCurrencyPairs: %w", err)
}
Expand All @@ -128,18 +137,19 @@ func validateAndConvertPriceFeedDataTx(
}
}

// Create transactions to initialize the currency pair, and add it to the list of pairs to set

// pack arguments for calling the `initializeCurrencyPair` function on the oracle contract
args := []interface{}{currencyPairHash, uint8(price.Decimals)}
calldata, err := abi.Pack("initializeCurrencyPair", args...)
calldata, err := oracleAbi.Pack("initializeCurrencyPair", args...)
if err != nil {
return nil, fmt.Errorf("failed to pack args for initializeCurrencyPair: %w", err)
}

txdata := types.InjectedTx{
From: cfg.oracleCallerAddress,
From: *cfg.oracleCallerAddress,
Value: new(big.Int),
Gas: 100000,
To: &cfg.oracleContractAddress,
Gas: 0,
To: cfg.oracleContractAddress,
Data: calldata,
SourceTransactionId: sequencerBlockHash,
SourceTransactionIndex: 0,
Expand All @@ -152,17 +162,17 @@ func validateAndConvertPriceFeedDataTx(
}

args := []interface{}{currencyPairsToSet, pricesToSet}
calldata, err := abi.Pack("setPrices", args...)
calldata, err := oracleAbi.Pack("setPrices", args...)
if err != nil {
return nil, err
}

txdata := types.InjectedTx{
From: cfg.oracleCallerAddress,
From: *cfg.oracleCallerAddress,
Value: new(big.Int),
// TODO: max gas costs; proportional to the amount of pairs being updated
Gas: 900000,
To: &cfg.oracleContractAddress,
Gas: 0,
To: cfg.oracleContractAddress,
Data: calldata,
SourceTransactionId: sequencerBlockHash,
SourceTransactionIndex: 0,
Expand Down Expand Up @@ -222,7 +232,7 @@ func validateAndConvertDepositTx(
//
// the fees are spent from the "bridge account" which is not actually a real account, but is instead some
// address defined by consensus, so the gas cost is not actually deducted from any account.
Gas: 64000,
Gas: 0,
To: &bac.Erc20Asset.ContractAddress,
Data: calldata,
SourceTransactionId: deposit.SourceTransactionId.Inner,
Expand Down Expand Up @@ -355,7 +365,7 @@ func validateStaticExecutedBlockMetadata(block *astriaPb.ExecutedBlockMetadata,
}

func callContract(abi *abi.ABI, evm *vm.EVM, from common.Address, to common.Address, methodName string, args ...interface{}) ([]interface{}, error) {
const CONTRACT_CALL_GAS = 100000 // gas is arbitrary
const CONTRACT_CALL_GAS = ^uint64(0) // gas is arbitrary

calldata, err := abi.Pack(methodName, args...)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions params/astria_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ type AstriaEIP1559Params struct {
}

type AstriaOracleConfig struct {
ContractAddress common.Address `json:"contractAddress"`
CallerAddress common.Address `json:"callerAddress"`
ContractAddress *common.Address `json:"contractAddress"`
CallerAddress *common.Address `json:"callerAddress"`
}

func (c *ChainConfig) AstriaExtraData(height uint64) []byte {
Expand Down
Loading