diff --git a/README-docker.md b/README-docker.md index f4364fe8..f785bb88 100644 --- a/README-docker.md +++ b/README-docker.md @@ -54,6 +54,7 @@ These are the environment variables you can edit and their default values: | `AUTOCONFIGURE_PUBLIC_IP` | `0` | Set to `1` to autoconfigure `PUBLIC_IP`, skipped if PUBLIC_IP is set | | `AUTOCONFIGURE_BOOTSTRAP` | `0` | Set to `1` to autoconfigure `BOOTSTRAP_IPS` and `BOOTSTRAP_IDS` | | `AUTOCONFIGURE_BOOTSTRAP_ENDPOINT` | `https://coston2.flare.network/ext/info` | Endpoint used for [bootstrapping](https://docs.avax.network/nodes/maintain/avalanchego-config-flags#bootstrapping) when `AUTOCONFIGURE_BOOTSTRAP` is enabled. Possible values are `https://coston2.flare.network/ext/info` or `https://flare.flare.network/ext/info`. | +| `AUTOCONFIGURE_FALLBACK_ENDPOINTS` | _(empty)_ | Comma-divided fallback bootstrap endpoints, used if `AUTOCONFIGURE_BOOTSTRAP_ENDPOINT` is not valid (not whitelisted / unreachable / etc), tested from first-to-last until one is valid | | `BOOTSTRAP_BEACON_CONNECTION_TIMEOUT` | `1m` | Set the duration value (eg. `45s` / `5m` / `1h`) for [--bootstrap-beacon-connection-timeout](https://docs.avax.network/nodes/maintain/avalanchego-config-flags#--bootstrap-beacon-connection-timeout-duration) AvalancheGo flag. | | `EXTRA_ARGUMENTS` | | Extra arguments passed to flare binary | diff --git a/avalanchego/version/constants.go b/avalanchego/version/constants.go index ab2809fd..57654a75 100644 --- a/avalanchego/version/constants.go +++ b/avalanchego/version/constants.go @@ -14,7 +14,7 @@ var ( Current = &Semantic{ Major: 1, Minor: 7, - Patch: 1806, + Patch: 1807, } CurrentApp = &Application{ Major: Current.Major, diff --git a/coreth/core/daemon.go b/coreth/core/daemon.go index 90bd8cc7..acf55eb3 100644 --- a/coreth/core/daemon.go +++ b/coreth/core/daemon.go @@ -6,11 +6,26 @@ package core import ( "fmt" "math/big" + "os" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/ava-labs/coreth/core/vm" + "github.com/ava-labs/coreth/params" +) + +var ( + // Define activation times for submitter contract + submitterContractActivationTimeFlare = big.NewInt(time.Date(2024, time.March, 26, 12, 0, 0, 0, time.UTC).Unix()) + submitterContractActivationTimeCostwo = big.NewInt(time.Date(2024, time.March, 7, 12, 0, 0, 0, time.UTC).Unix()) + + // Define ftso and submitter contract addresses + prioritisedFTSOContractAddress = common.HexToAddress("0x1000000000000000000000000000000000000003") + + prioritisedSubmitterContractAddress = common.HexToAddress("0x2cA6571Daa15ce734Bbd0Bf27D5C9D16787fc33f") + prioritisedSubmitterContractAddressEnv = common.HexToAddress(os.Getenv("SUBMITTER_CONTRACT_ADDRESS")) // for local and staging chains ) // Define errors @@ -73,10 +88,35 @@ func GetDaemonSelector(blockTime *big.Int) []byte { } } -func GetPrioritisedFTSOContract(blockTime *big.Int) string { +func isPrioritisedFTSOContract(to *common.Address) bool { + return to != nil && *to == prioritisedFTSOContractAddress +} + +func isPrioritisedSubmitterContract(chainID *big.Int, to *common.Address, blockTime *big.Int) bool { switch { + case to == nil || chainID == nil || blockTime == nil: + return false + case chainID.Cmp(params.FlareChainID) == 0: + return *to == prioritisedSubmitterContractAddress && + blockTime.Cmp(submitterContractActivationTimeFlare) > 0 + case chainID.Cmp(params.CostwoChainID) == 0: + return *to == prioritisedSubmitterContractAddress && + blockTime.Cmp(submitterContractActivationTimeCostwo) > 0 + case chainID.Cmp(params.LocalFlareChainID) == 0 || chainID.Cmp(params.StagingChainID) == 0: + return *to == prioritisedSubmitterContractAddressEnv default: - return "0x1000000000000000000000000000000000000003" + return false + } +} + +func IsPrioritisedContractCall(chainID *big.Int, to *common.Address, ret []byte, blockTime *big.Int) bool { + switch { + case isPrioritisedFTSOContract(to): + return true + case isPrioritisedSubmitterContract(chainID, to, blockTime): + return !isZeroSlice(ret) + default: + return false } } @@ -157,3 +197,12 @@ func atomicDaemonAndMint(evm EVMCaller, log log.Logger) { log.Warn("Daemon error", "error", daemonErr) } } + +func isZeroSlice(s []byte) bool { + for i := len(s) - 1; i >= 0; i-- { + if s[i] != 0 { + return false + } + } + return true +} diff --git a/coreth/core/daemon_test.go b/coreth/core/daemon_test.go index 88d224da..7be96a2f 100644 --- a/coreth/core/daemon_test.go +++ b/coreth/core/daemon_test.go @@ -7,11 +7,13 @@ import ( "errors" "math/big" "testing" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/ava-labs/coreth/core/vm" + "github.com/ava-labs/coreth/params" ) // Define a mock structure to spy and mock values for daemon calls @@ -476,3 +478,31 @@ func TestDaemonShouldNotMintMoreThanLimit(t *testing.T) { t.Errorf("Add balance call count not as expected. got %d want 1", defaultEVMMock.mockEVMCallerData.addBalanceCalls) } } + +func TestPrioritisedContract(t *testing.T) { + address := common.HexToAddress("0x123456789aBCdEF123456789aBCdef123456789A") + preForkTime := big.NewInt(time.Date(2024, time.March, 20, 12, 0, 0, 0, time.UTC).Unix()) + postForkTime := big.NewInt(time.Date(2024, time.March, 27, 12, 0, 0, 0, time.UTC).Unix()) + ret0 := [32]byte{} + ret1 := [32]byte{} + ret1[31] = 1 + + if IsPrioritisedContractCall(params.FlareChainID, &address, nil, preForkTime) { + t.Errorf("Expected false for wrong address") + } + if !IsPrioritisedContractCall(params.FlareChainID, &prioritisedFTSOContractAddress, nil, preForkTime) { + t.Errorf("Expected true for FTSO contract") + } + if IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddress, ret1[:], preForkTime) { + t.Errorf("Expected false for submitter contract before activation") + } + if !IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddress, ret1[:], postForkTime) { + t.Errorf("Expected true for submitter contract after activation") + } + if IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddress, ret0[:], postForkTime) { + t.Errorf("Expected false for submitter contract with wrong return value") + } + if IsPrioritisedContractCall(params.FlareChainID, &prioritisedSubmitterContractAddress, nil, postForkTime) { + t.Errorf("Expected false for submitter contract with no return value") + } +} diff --git a/coreth/core/state_connector.go b/coreth/core/state_connector.go index 06993612..2792735b 100644 --- a/coreth/core/state_connector.go +++ b/coreth/core/state_connector.go @@ -115,16 +115,30 @@ func FinaliseRoundSelector(chainID *big.Int, blockTime *big.Int) []byte { func GetDefaultAttestors(chainID *big.Int, blockTime *big.Int) []common.Address { switch { case chainID.Cmp(params.FlareChainID) == 0: - return []common.Address{ - common.HexToAddress("0x0988Cf4828F4e4eD0cE7c07467E70e19095Ee152"), - common.HexToAddress("0x6BC7DCa62010D418eB72CCdc58561e00C5868Ef1"), - common.HexToAddress("0xE34Bb361536610a9DCcEa5292262e36AfF65c06c"), - common.HexToAddress("0x8A3D627D86A81F5D21683F4963565C63DB5e1309"), - common.HexToAddress("0x2D3e7e4b19bDc920fd9C57BD3072A31F5a59FeC8"), - common.HexToAddress("0x6455dC38fdF739b6fE021b30C7D9672C1c6DEb5c"), - common.HexToAddress("0x49893c5Dfc035F4eE4E46faC014f6D4bC80F7f92"), - common.HexToAddress("0x08e8b2Af4874e920de27723576A13d66008Af523"), - common.HexToAddress("0x5D2f75392DdDa69a2818021dd6a64937904c8352"), + if blockTime.Cmp(submitterContractActivationTimeFlare) > 0 { + return []common.Address{ + common.HexToAddress("0x4E07E1F3DB3Dc9BAd56Cc829747cc0148234329F"), + common.HexToAddress("0xB264Fad6Fdc65767998f93501945aB8F9108809d"), + common.HexToAddress("0x366BeC54195bfD45DBB34b79Ad2dEC4010598947"), + common.HexToAddress("0x2665B179d5fCE1118f06e23B5d6E7617c5Ff733A"), + common.HexToAddress("0x65cBaFaDD7C914179aabcE9C35f918a4E36AfFf9"), + common.HexToAddress("0x7eC6a7C7c4Ef003A75DC6c06352B48B37Ac2191B"), + common.HexToAddress("0xEa9bC2F98eFFC6A27E2C31733c1905961826f73B"), + common.HexToAddress("0xA4aA75a9B49c7f2B4be62b2999d7103E78D004C7"), + common.HexToAddress("0x4DF8436D7578C2d3bc73d33B6644913e131B70FC"), + } + } else { + return []common.Address{ + common.HexToAddress("0x0988Cf4828F4e4eD0cE7c07467E70e19095Ee152"), + common.HexToAddress("0x6BC7DCa62010D418eB72CCdc58561e00C5868Ef1"), + common.HexToAddress("0xE34Bb361536610a9DCcEa5292262e36AfF65c06c"), + common.HexToAddress("0x8A3D627D86A81F5D21683F4963565C63DB5e1309"), + common.HexToAddress("0x2D3e7e4b19bDc920fd9C57BD3072A31F5a59FeC8"), + common.HexToAddress("0x6455dC38fdF739b6fE021b30C7D9672C1c6DEb5c"), + common.HexToAddress("0x49893c5Dfc035F4eE4E46faC014f6D4bC80F7f92"), + common.HexToAddress("0x08e8b2Af4874e920de27723576A13d66008Af523"), + common.HexToAddress("0x5D2f75392DdDa69a2818021dd6a64937904c8352"), + } } case chainID.Cmp(params.SongbirdChainID) == 0: return []common.Address{ diff --git a/coreth/core/state_transition.go b/coreth/core/state_transition.go index d6a49bb2..77d815d3 100644 --- a/coreth/core/state_transition.go +++ b/coreth/core/state_transition.go @@ -56,8 +56,10 @@ The state transitioning model does all the necessary work to work out a valid ne 3) Create a new state object if the recipient is \0*32 4) Value transfer == If contract creation == - 4a) Attempt to run transaction data - 4b) If valid, use result as code for the new state object + + 4a) Attempt to run transaction data + 4b) If valid, use result as code for the new state object + == end == 5) Run Script section 6) Derive new state root @@ -300,13 +302,13 @@ func (st *StateTransition) preCheck() error { // TransitionDb will transition the state by applying the current message and // returning the evm execution result with following fields. // -// - used gas: -// total gas used (including gas being refunded) -// - returndata: -// the returned data from evm -// - concrete execution error: -// various **EVM** error which aborts the execution, -// e.g. ErrOutOfGas, ErrExecutionReverted +// - used gas: +// total gas used (including gas being refunded) +// - returndata: +// the returned data from evm +// - concrete execution error: +// various **EVM** error which aborts the execution, +// e.g. ErrOutOfGas, ErrExecutionReverted // // However if any consensus issue encountered, return the error directly with // nil evm execution result. @@ -417,8 +419,8 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } st.refundGas(rules.IsApricotPhase1) - if vmerr == nil && msg.To() != nil && *msg.To() == common.HexToAddress(GetPrioritisedFTSOContract(timestamp)) && st.initialGas <= GetMaxFTSOGasLimit(timestamp) { - nominalGasUsed := uint64(params.TxGas) // 21000 + if vmerr == nil && IsPrioritisedContractCall(chainID, msg.To(), ret, timestamp) && st.initialGas <= GetMaxFTSOGasLimit(timestamp) { + nominalGasUsed := params.TxGas // 21000 nominalGasPrice := uint64(params.ApricotPhase4MinBaseFee) // 25_000_000_000; the max base fee is 1_000_000_000_000 nominalFee := new(big.Int).Mul(new(big.Int).SetUint64(nominalGasUsed), new(big.Int).SetUint64(nominalGasPrice)) actualGasUsed := st.gasUsed() diff --git a/entrypoint.sh b/entrypoint.sh old mode 100644 new mode 100755 index 4550351e..4dfc5707 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -14,19 +14,39 @@ then fi fi -# Check if we can connect to the bootstrap endpoint (whitelisting) -BOOTSTRAP_STATUS=$(curl -m 10 -s -w %{http_code} -X POST --data '{ "jsonrpc":"2.0", "id":1, "method":"info.getNodeIP" }' -H 'content-type:application/json;' "$AUTOCONFIGURE_BOOTSTRAP_ENDPOINT" -o /dev/null) -if [ "$BOOTSTRAP_STATUS" != "200" ]; then - echo "Could not connect to bootstrap endpoint. Is your IP whitelisted?" - exit 1 -fi - if [ "$AUTOCONFIGURE_BOOTSTRAP" = "1" ]; then + + + __BOOTSTRAP_ENDPOINTS=("${AUTOCONFIGURE_BOOTSTRAP_ENDPOINT}" ${AUTOCONFIGURE_FALLBACK_ENDPOINTS//,/ }) + + echo "Trying provided bootstrap endpoints" + for __ENDPOINT in "${__BOOTSTRAP_ENDPOINTS[@]}"; do + echo " Trying endpoint $__ENDPOINT" + + RESPONSE_CODE=$(curl -X POST -m 5 -s -o /dev/null -w '%{http_code}' "$__ENDPOINT" -H 'Content-Type: application/json' --data '{ "jsonrpc":"2.0", "id":1, "method":"info.getNodeIP" }' || true) + if [ "$RESPONSE_CODE" = "200" ]; then + __BOOTSTRAP_ENDPOINT="$__ENDPOINT" + break + else + echo " Failed! The endpoint is unreachable." + continue + fi + done + + if [ -z "$__BOOTSTRAP_ENDPOINT" ]; then + echo " None of provided bootstrap endpoints worked!" + exit 1 + fi + + echo "Autoconfiguring bootstrap IPs and IDs" - BOOTSTRAP_IPS=$(curl -m 10 -sX POST --data '{ "jsonrpc":"2.0", "id":1, "method":"info.getNodeIP" }' -H 'content-type:application/json;' "$AUTOCONFIGURE_BOOTSTRAP_ENDPOINT" | jq -r ".result.ip") - BOOTSTRAP_IDS=$(curl -m 10 -sX POST --data '{ "jsonrpc":"2.0", "id":1, "method":"info.getNodeID" }' -H 'content-type:application/json;' "$AUTOCONFIGURE_BOOTSTRAP_ENDPOINT" | jq -r ".result.nodeID") + BOOTSTRAP_IPS=$(curl -m 10 -sX POST --data '{ "jsonrpc":"2.0", "id":1, "method":"info.getNodeIP" }' -H 'content-type:application/json;' "$__BOOTSTRAP_ENDPOINT" | jq -r ".result.ip") + BOOTSTRAP_IDS=$(curl -m 10 -sX POST --data '{ "jsonrpc":"2.0", "id":1, "method":"info.getNodeID" }' -H 'content-type:application/json;' "$__BOOTSTRAP_ENDPOINT" | jq -r ".result.nodeID") + + echo " Got bootstrap ips: '${BOOTSTRAP_IPS}'" + echo " Got bootstrap ids: '${BOOTSTRAP_IDS}'" fi exec /app/build/avalanchego \