mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 22:26:42 +00:00
Merge pull request #1204 from maticnetwork/consistent-revert
Add: Changes reverted in PR#1124
This commit is contained in:
commit
4a03cc04c1
10 changed files with 298 additions and 32 deletions
|
|
@ -883,6 +883,10 @@ func (c *Bor) changeContractCodeIfNeeded(headerNumber uint64, state *state.State
|
||||||
for addr, account := range allocs {
|
for addr, account := range allocs {
|
||||||
log.Info("change contract code", "address", addr)
|
log.Info("change contract code", "address", addr)
|
||||||
state.SetCode(addr, account.Code)
|
state.SetCode(addr, account.Code)
|
||||||
|
|
||||||
|
if state.GetBalance(addr).Cmp(big.NewInt(0)) == 0 {
|
||||||
|
state.SetBalance(addr, account.Balance)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,12 @@ func TestGenesisContractChange(t *testing.T) {
|
||||||
"balance": "0x1000",
|
"balance": "0x1000",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"6": map[string]interface{}{
|
||||||
|
addr0.Hex(): map[string]interface{}{
|
||||||
|
"code": hexutil.Bytes{0x1, 0x4},
|
||||||
|
"balance": "0x2000",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -87,24 +93,35 @@ func TestGenesisContractChange(t *testing.T) {
|
||||||
|
|
||||||
root := genesis.Root()
|
root := genesis.Root()
|
||||||
|
|
||||||
// code does not change
|
// code does not change, balance remains 0
|
||||||
root, statedb = addBlock(root, 1)
|
root, statedb = addBlock(root, 1)
|
||||||
require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x1})
|
require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x1})
|
||||||
|
require.Equal(t, statedb.GetBalance(addr0), big.NewInt(0))
|
||||||
|
|
||||||
// code changes 1st time
|
// code changes 1st time, balance remains 0
|
||||||
root, statedb = addBlock(root, 2)
|
root, statedb = addBlock(root, 2)
|
||||||
require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x2})
|
require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x2})
|
||||||
|
require.Equal(t, statedb.GetBalance(addr0), big.NewInt(0))
|
||||||
|
|
||||||
// code same as 1st change
|
// code same as 1st change, balance remains 0
|
||||||
root, statedb = addBlock(root, 3)
|
root, statedb = addBlock(root, 3)
|
||||||
require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x2})
|
require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x2})
|
||||||
|
|
||||||
// code changes 2nd time
|
|
||||||
_, statedb = addBlock(root, 4)
|
|
||||||
require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x3})
|
|
||||||
|
|
||||||
// make sure balance change DOES NOT take effect
|
|
||||||
require.Equal(t, statedb.GetBalance(addr0), big.NewInt(0))
|
require.Equal(t, statedb.GetBalance(addr0), big.NewInt(0))
|
||||||
|
|
||||||
|
// code changes 2nd time, balance updates to 4096
|
||||||
|
root, statedb = addBlock(root, 4)
|
||||||
|
require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x3})
|
||||||
|
require.Equal(t, statedb.GetBalance(addr0), big.NewInt(4096))
|
||||||
|
|
||||||
|
// code same as 2nd change, balance remains 4096
|
||||||
|
root, statedb = addBlock(root, 5)
|
||||||
|
require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x3})
|
||||||
|
require.Equal(t, statedb.GetBalance(addr0), big.NewInt(4096))
|
||||||
|
|
||||||
|
// code changes 3rd time, balance remains 4096
|
||||||
|
_, statedb = addBlock(root, 6)
|
||||||
|
require.Equal(t, statedb.GetCode(addr0), []byte{0x1, 0x4})
|
||||||
|
require.Equal(t, statedb.GetBalance(addr0), big.NewInt(4096))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncodeSigHeaderJaipur(t *testing.T) {
|
func TestEncodeSigHeaderJaipur(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,7 @@ var DefaultConfig = Config{
|
||||||
GlobalQueue: 1024,
|
GlobalQueue: 1024,
|
||||||
|
|
||||||
Lifetime: 3 * time.Hour,
|
Lifetime: 3 * time.Hour,
|
||||||
|
AllowUnprotectedTxs: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
// sanitize checks the provided user configurations and changes anything that's
|
// sanitize checks the provided user configurations and changes anything that's
|
||||||
|
|
@ -602,6 +603,7 @@ func (pool *LegacyPool) local() map[common.Address]types.Transactions {
|
||||||
func (pool *LegacyPool) validateTxBasics(tx *types.Transaction, local bool) error {
|
func (pool *LegacyPool) validateTxBasics(tx *types.Transaction, local bool) error {
|
||||||
opts := &txpool.ValidationOptions{
|
opts := &txpool.ValidationOptions{
|
||||||
Config: pool.chainconfig,
|
Config: pool.chainconfig,
|
||||||
|
AllowUnprotectedTxs: pool.config.AllowUnprotectedTxs,
|
||||||
Accept: 0 |
|
Accept: 0 |
|
||||||
1<<types.LegacyTxType |
|
1<<types.LegacyTxType |
|
||||||
1<<types.AccessListTxType |
|
1<<types.AccessListTxType |
|
||||||
|
|
@ -671,6 +673,11 @@ func (pool *LegacyPool) add(tx *types.Transaction, local bool) (replaced bool, e
|
||||||
knownTxMeter.Mark(1)
|
knownTxMeter.Mark(1)
|
||||||
return false, txpool.ErrAlreadyKnown
|
return false, txpool.ErrAlreadyKnown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pool.config.AllowUnprotectedTxs {
|
||||||
|
pool.signer = types.NewFakeSigner(tx.ChainId())
|
||||||
|
}
|
||||||
|
|
||||||
// Make the local flag. If it's from local source or it's from the network but
|
// Make the local flag. If it's from local source or it's from the network but
|
||||||
// the sender is marked as local previously, treat it as the local transaction.
|
// the sender is marked as local previously, treat it as the local transaction.
|
||||||
isLocal := local || pool.locals.containsTx(tx)
|
isLocal := local || pool.locals.containsTx(tx)
|
||||||
|
|
@ -984,6 +991,11 @@ func (pool *LegacyPool) Add(txs []*types.Transaction, local, sync bool) []error
|
||||||
knownTxMeter.Mark(1)
|
knownTxMeter.Mark(1)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pool.config.AllowUnprotectedTxs {
|
||||||
|
pool.signer = types.NewFakeSigner(tx.ChainId())
|
||||||
|
}
|
||||||
|
|
||||||
// Exclude transactions with basic errors, e.g invalid signatures and
|
// Exclude transactions with basic errors, e.g invalid signatures and
|
||||||
// insufficient intrinsic gas as soon as possible and cache senders
|
// insufficient intrinsic gas as soon as possible and cache senders
|
||||||
// in transactions before obtaining lock
|
// in transactions before obtaining lock
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@ import (
|
||||||
type ValidationOptions struct {
|
type ValidationOptions struct {
|
||||||
Config *params.ChainConfig // Chain configuration to selectively validate based on current fork rules
|
Config *params.ChainConfig // Chain configuration to selectively validate based on current fork rules
|
||||||
|
|
||||||
|
AllowUnprotectedTxs bool // Whether to allow unprotected transactions in the pool
|
||||||
|
|
||||||
Accept uint8 // Bitmap of transaction types that should be accepted for the calling pool
|
Accept uint8 // Bitmap of transaction types that should be accepted for the calling pool
|
||||||
MaxSize uint64 // Maximum size of a transaction that the caller can meaningfully handle
|
MaxSize uint64 // Maximum size of a transaction that the caller can meaningfully handle
|
||||||
MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool
|
MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool
|
||||||
|
|
@ -91,7 +93,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
||||||
return core.ErrTipAboveFeeCap
|
return core.ErrTipAboveFeeCap
|
||||||
}
|
}
|
||||||
// Make sure the transaction is signed properly
|
// Make sure the transaction is signed properly
|
||||||
if _, err := types.Sender(signer, tx); err != nil {
|
if _, err := types.Sender(signer, tx); err != nil && !opts.AllowUnprotectedTxs {
|
||||||
return ErrInvalidSender
|
return ErrInvalidSender
|
||||||
}
|
}
|
||||||
// Ensure the transaction has more gas than the bare minimum needed to cover
|
// Ensure the transaction has more gas than the bare minimum needed to cover
|
||||||
|
|
|
||||||
|
|
@ -582,3 +582,38 @@ func deriveChainId(v *big.Int) *big.Int {
|
||||||
v = new(big.Int).Sub(v, big.NewInt(35))
|
v = new(big.Int).Sub(v, big.NewInt(35))
|
||||||
return v.Div(v, big.NewInt(2))
|
return v.Div(v, big.NewInt(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FakeSigner implements the Signer interface and accepts unprotected transactions
|
||||||
|
type FakeSigner struct{ londonSigner }
|
||||||
|
|
||||||
|
var _ Signer = FakeSigner{}
|
||||||
|
|
||||||
|
func NewFakeSigner(chainId *big.Int) Signer {
|
||||||
|
signer := NewLondonSigner(chainId)
|
||||||
|
ls, _ := signer.(londonSigner)
|
||||||
|
return FakeSigner{londonSigner: ls}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FakeSigner) Sender(tx *Transaction) (common.Address, error) {
|
||||||
|
return f.londonSigner.Sender(tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FakeSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
|
||||||
|
return f.londonSigner.SignatureValues(tx, sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FakeSigner) ChainID() *big.Int {
|
||||||
|
return f.londonSigner.ChainID()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash returns 'signature hash', i.e. the transaction hash that is signed by the
|
||||||
|
// private key. This hash does not uniquely identify the transaction.
|
||||||
|
func (f FakeSigner) Hash(tx *Transaction) common.Hash {
|
||||||
|
return f.londonSigner.Hash(tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true if the given signer is the same as the receiver.
|
||||||
|
func (f FakeSigner) Equal(Signer) bool {
|
||||||
|
// Always return true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -174,8 +174,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
|
|
||||||
eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil}
|
eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil}
|
||||||
if eth.APIBackend.allowUnprotectedTxs {
|
if eth.APIBackend.allowUnprotectedTxs {
|
||||||
log.Debug(" ###########", "Unprotected transactions allowed")
|
log.Info("------Unprotected transactions allowed-------")
|
||||||
|
|
||||||
config.TxPool.AllowUnprotectedTxs = true
|
config.TxPool.AllowUnprotectedTxs = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,21 +83,6 @@ func (b *EthAPIBackend) GetVoteOnHash(ctx context.Context, starBlockNr uint64, e
|
||||||
return false, fmt.Errorf("Hash mismatch: localChainHash %s, milestoneHash %s", localEndBlockHash, hash)
|
return false, fmt.Errorf("Hash mismatch: localChainHash %s, milestoneHash %s", localEndBlockHash, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
ethHandler := (*ethHandler)(b.eth.handler)
|
|
||||||
|
|
||||||
bor, ok := ethHandler.chain.Engine().(*bor.Bor)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return false, fmt.Errorf("Bor not available")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = bor.HeimdallClient.FetchMilestoneID(ctx, milestoneId)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
downloader.UnlockMutex(false, "", endBlockNr, common.Hash{})
|
|
||||||
return false, fmt.Errorf("Milestone ID doesn't exist in Heimdall")
|
|
||||||
}
|
|
||||||
|
|
||||||
downloader.UnlockMutex(true, milestoneId, endBlockNr, localEndBlock.Hash())
|
downloader.UnlockMutex(true, milestoneId, endBlockNr, localEndBlock.Hash())
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
|
|
|
||||||
|
|
@ -42,3 +42,22 @@ func TestConfigLegacy(t *testing.T) {
|
||||||
readFile("./testdata/test.toml")
|
readFile("./testdata/test.toml")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDefaultConfigLegacy(t *testing.T) {
|
||||||
|
readFile := func(path string) {
|
||||||
|
expectedConfig, err := readLegacyConfig(path)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
testConfig := DefaultConfig()
|
||||||
|
|
||||||
|
testConfig.Identity = "Polygon-Devs"
|
||||||
|
testConfig.DataDir = "/var/lib/bor"
|
||||||
|
|
||||||
|
assert.Equal(t, expectedConfig, testConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// read file in hcl format
|
||||||
|
t.Run("toml", func(t *testing.T) {
|
||||||
|
readFile("./testdata/default.toml")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
193
internal/cli/server/testdata/default.toml
vendored
Normal file
193
internal/cli/server/testdata/default.toml
vendored
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
chain = "mainnet"
|
||||||
|
identity = "Polygon-Devs"
|
||||||
|
verbosity = 3
|
||||||
|
log-level = ""
|
||||||
|
vmdebug = false
|
||||||
|
datadir = "/var/lib/bor"
|
||||||
|
ancient = ""
|
||||||
|
"db.engine" = "leveldb"
|
||||||
|
keystore = ""
|
||||||
|
"rpc.batchlimit" = 100
|
||||||
|
"rpc.returndatalimit" = 100000
|
||||||
|
syncmode = "full"
|
||||||
|
gcmode = "full"
|
||||||
|
snapshot = true
|
||||||
|
"bor.logs" = false
|
||||||
|
ethstats = ""
|
||||||
|
devfakeauthor = false
|
||||||
|
|
||||||
|
["eth.requiredblocks"]
|
||||||
|
|
||||||
|
[log]
|
||||||
|
vmodule = ""
|
||||||
|
json = false
|
||||||
|
backtrace = ""
|
||||||
|
debug = false
|
||||||
|
|
||||||
|
[p2p]
|
||||||
|
maxpeers = 50
|
||||||
|
maxpendpeers = 50
|
||||||
|
bind = "0.0.0.0"
|
||||||
|
port = 30303
|
||||||
|
nodiscover = false
|
||||||
|
nat = "any"
|
||||||
|
netrestrict = ""
|
||||||
|
nodekey = ""
|
||||||
|
nodekeyhex = ""
|
||||||
|
txarrivalwait = "500ms"
|
||||||
|
[p2p.discovery]
|
||||||
|
v4disc = true
|
||||||
|
v5disc = false
|
||||||
|
bootnodes = []
|
||||||
|
bootnodesv4 = []
|
||||||
|
bootnodesv5 = []
|
||||||
|
static-nodes = []
|
||||||
|
trusted-nodes = []
|
||||||
|
dns = []
|
||||||
|
|
||||||
|
[heimdall]
|
||||||
|
url = "http://localhost:1317"
|
||||||
|
"bor.without" = false
|
||||||
|
grpc-address = ""
|
||||||
|
"bor.runheimdall" = false
|
||||||
|
"bor.runheimdallargs" = ""
|
||||||
|
"bor.useheimdallapp" = false
|
||||||
|
|
||||||
|
[txpool]
|
||||||
|
locals = []
|
||||||
|
nolocals = false
|
||||||
|
journal = "transactions.rlp"
|
||||||
|
rejournal = "1h0m0s"
|
||||||
|
pricelimit = 1
|
||||||
|
pricebump = 10
|
||||||
|
accountslots = 16
|
||||||
|
globalslots = 32768
|
||||||
|
accountqueue = 16
|
||||||
|
globalqueue = 32768
|
||||||
|
lifetime = "3h0m0s"
|
||||||
|
|
||||||
|
[miner]
|
||||||
|
mine = false
|
||||||
|
etherbase = ""
|
||||||
|
extradata = ""
|
||||||
|
gaslimit = 30000000
|
||||||
|
gasprice = "1000000000"
|
||||||
|
recommit = "2m5s"
|
||||||
|
commitinterrupt = true
|
||||||
|
|
||||||
|
[jsonrpc]
|
||||||
|
ipcdisable = false
|
||||||
|
ipcpath = ""
|
||||||
|
gascap = 50000000
|
||||||
|
evmtimeout = "5s"
|
||||||
|
txfeecap = 1.0
|
||||||
|
allow-unprotected-txs = false
|
||||||
|
enabledeprecatedpersonal = false
|
||||||
|
[jsonrpc.http]
|
||||||
|
enabled = false
|
||||||
|
port = 8545
|
||||||
|
prefix = ""
|
||||||
|
host = "localhost"
|
||||||
|
api = ["eth", "net", "web3", "txpool", "bor"]
|
||||||
|
vhosts = ["localhost"]
|
||||||
|
corsdomain = ["localhost"]
|
||||||
|
ep-size = 40
|
||||||
|
ep-requesttimeout = "0s"
|
||||||
|
[jsonrpc.ws]
|
||||||
|
enabled = false
|
||||||
|
port = 8546
|
||||||
|
prefix = ""
|
||||||
|
host = "localhost"
|
||||||
|
api = ["net", "web3"]
|
||||||
|
origins = ["localhost"]
|
||||||
|
ep-size = 40
|
||||||
|
ep-requesttimeout = "0s"
|
||||||
|
[jsonrpc.graphql]
|
||||||
|
enabled = false
|
||||||
|
port = 0
|
||||||
|
prefix = ""
|
||||||
|
host = ""
|
||||||
|
vhosts = ["localhost"]
|
||||||
|
corsdomain = ["localhost"]
|
||||||
|
ep-size = 0
|
||||||
|
ep-requesttimeout = ""
|
||||||
|
[jsonrpc.auth]
|
||||||
|
jwtsecret = ""
|
||||||
|
addr = "localhost"
|
||||||
|
port = 8551
|
||||||
|
vhosts = ["localhost"]
|
||||||
|
[jsonrpc.timeouts]
|
||||||
|
read = "10s"
|
||||||
|
write = "30s"
|
||||||
|
idle = "2m0s"
|
||||||
|
|
||||||
|
[gpo]
|
||||||
|
blocks = 20
|
||||||
|
percentile = 60
|
||||||
|
maxheaderhistory = 1024
|
||||||
|
maxblockhistory = 1024
|
||||||
|
maxprice = "500000000000"
|
||||||
|
ignoreprice = "2"
|
||||||
|
|
||||||
|
[telemetry]
|
||||||
|
metrics = false
|
||||||
|
expensive = false
|
||||||
|
prometheus-addr = "127.0.0.1:7071"
|
||||||
|
opencollector-endpoint = ""
|
||||||
|
[telemetry.influx]
|
||||||
|
influxdb = false
|
||||||
|
endpoint = ""
|
||||||
|
database = ""
|
||||||
|
username = ""
|
||||||
|
password = ""
|
||||||
|
influxdbv2 = false
|
||||||
|
token = ""
|
||||||
|
bucket = ""
|
||||||
|
organization = ""
|
||||||
|
[telemetry.influx.tags]
|
||||||
|
|
||||||
|
[cache]
|
||||||
|
cache = 1024
|
||||||
|
gc = 25
|
||||||
|
snapshot = 10
|
||||||
|
database = 50
|
||||||
|
trie = 15
|
||||||
|
noprefetch = false
|
||||||
|
preimages = false
|
||||||
|
txlookuplimit = 2350000
|
||||||
|
triesinmemory = 128
|
||||||
|
blocklogs = 32
|
||||||
|
timeout = "1h0m0s"
|
||||||
|
fdlimit = 0
|
||||||
|
|
||||||
|
[leveldb]
|
||||||
|
compactiontablesize = 2
|
||||||
|
compactiontablesizemultiplier = 1.0
|
||||||
|
compactiontotalsize = 10
|
||||||
|
compactiontotalsizemultiplier = 10.0
|
||||||
|
|
||||||
|
[accounts]
|
||||||
|
unlock = []
|
||||||
|
password = ""
|
||||||
|
allow-insecure-unlock = false
|
||||||
|
lightkdf = false
|
||||||
|
disable-bor-wallet = true
|
||||||
|
|
||||||
|
[grpc]
|
||||||
|
addr = ":3131"
|
||||||
|
|
||||||
|
[developer]
|
||||||
|
dev = false
|
||||||
|
period = 0
|
||||||
|
gaslimit = 11500000
|
||||||
|
|
||||||
|
[parallelevm]
|
||||||
|
enable = true
|
||||||
|
procs = 8
|
||||||
|
|
||||||
|
[pprof]
|
||||||
|
pprof = false
|
||||||
|
port = 6060
|
||||||
|
addr = "127.0.0.1"
|
||||||
|
memprofilerate = 524288
|
||||||
|
blockprofilerate = 0
|
||||||
|
|
@ -960,9 +960,9 @@ func (c *ChainConfig) CheckConfigForkOrder() error {
|
||||||
{name: "arrowGlacierBlock", block: c.ArrowGlacierBlock, optional: true},
|
{name: "arrowGlacierBlock", block: c.ArrowGlacierBlock, optional: true},
|
||||||
{name: "grayGlacierBlock", block: c.GrayGlacierBlock, optional: true},
|
{name: "grayGlacierBlock", block: c.GrayGlacierBlock, optional: true},
|
||||||
{name: "mergeNetsplitBlock", block: c.MergeNetsplitBlock, optional: true},
|
{name: "mergeNetsplitBlock", block: c.MergeNetsplitBlock, optional: true},
|
||||||
{name: "ShanghaiBlock", block: c.ShanghaiBlock},
|
{name: "shanghaiBlock", block: c.ShanghaiBlock},
|
||||||
{name: "CancunBlock", block: c.CancunBlock, optional: true},
|
{name: "cancunBlock", block: c.CancunBlock, optional: true},
|
||||||
{name: "pragueTime", block: c.PragueBlock, optional: true},
|
{name: "pragueBlock", block: c.PragueBlock, optional: true},
|
||||||
} {
|
} {
|
||||||
if lastFork.name != "" {
|
if lastFork.name != "" {
|
||||||
switch {
|
switch {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue