hive: engine simulator add jwt auth test cases

This commit is contained in:
jangko 2022-07-18 11:34:42 +07:00
parent 76e438da2a
commit a48f69a89a
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
6 changed files with 195 additions and 60 deletions

View File

@ -0,0 +1,109 @@
import
std/[base64, times, strutils],
test_env,
unittest2,
chronicles,
nimcrypto/[hmac, utils],
json_rpc/[rpcclient],
./types
# JWT Authentication Related
const
defaultJwtTokenSecretBytes = "secretsecretsecretsecretsecretse"
maxTimeDriftSeconds = 5'i64
defaultProtectedHeader = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"
proc base64urlEncode(x: auto): string =
base64.encode(x, safe = true).replace("=", "")
proc prepareAuthCallToken(secret: string, time: int64): string =
let key = cast[seq[byte]](secret)
let payload = """{"iat": $1}""" % [$time]
let token = defaultProtectedHeader & "." & payload.base64urlEncode
let sig = base64urlEncode(sha256.hmac(key, token).data)
token & "." & sig
proc getClient(t: TestEnv, token: string): RpcHttpClient =
proc authHeaders(): seq[(string, string)] =
@[("Authorization", "Bearer " & token)]
let client = newRpcHttpClient(getHeaders = authHeaders)
waitFor client.connect("localhost", t.conf.rpcPort, false)
return client
template genAuthTest(procName: untyped, timeDriftSeconds: int64, customAuthSecretBytes: string, authOK: bool) =
proc procName(t: TestEnv): TestStatus =
result = TestStatus.OK
# Default values
var
# All test cases send a simple TransitionConfigurationV1 to check the Authentication mechanism (JWT)
tConf = TransitionConfigurationV1(
terminalTotalDifficulty: t.ttd
)
testSecret = customAuthSecretBytes
testTime = getTime().toUnix
if testSecret.len == 0:
testSecret = defaultJwtTokenSecretBytes
if timeDriftSeconds != 0:
testTime = testTime + timeDriftSeconds
let token = prepareAuthCallToken(testSecret, testTime)
let client = getClient(t, token)
try:
discard waitFor client.call("engine_exchangeTransitionConfigurationV1", %[%tConf])
testCond authOk:
error "Authentication was supposed to fail authentication but passed"
except CatchableError:
testCond not authOk:
error "Authentication was supposed to pass authentication but failed"
genAuthTest(authTest1, 0'i64, "", true)
genAuthTest(authTest2, 0'i64, "secretsecretsecretsecretsecrets", false)
genAuthTest(authTest3, 0'i64, "\0secretsecretsecretsecretsecretse", false)
genAuthTest(authTest4, -1 - maxTimeDriftSeconds, "", false)
genAuthTest(authTest5, 1 - maxTimeDriftSeconds, "", true)
genAuthTest(authTest6, maxTimeDriftSeconds + 1, "", false)
genAuthTest(authTest7, maxTimeDriftSeconds - 1, "", true)
# JWT Authentication Tests
const authTestList* = [
TestSpec(
name: "JWT Authentication: No time drift, correct secret",
run: authTest1,
enableAuth: true
),
TestSpec(
name: "JWT Authentication: No time drift, incorrect secret (shorter)",
run: authTest2,
enableAuth: true
),
TestSpec(
name: "JWT Authentication: No time drift, incorrect secret (longer)",
run: authTest3,
enableAuth: true
),
TestSpec(
name: "JWT Authentication: Negative time drift, exceeding limit, correct secret",
run: authTest4,
enableAuth: true
),
TestSpec(
name: "JWT Authentication: Negative time drift, within limit, correct secret",
run: authTest5,
enableAuth: true
),
TestSpec(
name: "JWT Authentication: Positive time drift, exceeding limit, correct secret",
run: authTest6,
enableAuth: true
),
TestSpec(
name: "JWT Authentication: Positive time drift, within limit, correct secret",
run: authTest7,
enableAuth: true
)
]

View File

@ -7,7 +7,7 @@ import
web3/engine_api_types, web3/engine_api_types,
json_rpc/rpcclient, json_rpc/rpcclient,
../../../nimbus/merge/mergeutils, ../../../nimbus/merge/mergeutils,
../../../nimbus/[debug, constants], ../../../nimbus/[constants],
./engine_client ./engine_client
# Consensus Layer Client Mock used to sync the Execution Clients once the TTD has been reached # Consensus Layer Client Mock used to sync the Execution Clients once the TTD has been reached

View File

@ -1,15 +1,20 @@
import import
test_env, "."/[types, test_env, engine_tests, auths_tests],
engine_tests,
unittest2, unittest2,
../sim_utils ../sim_utils
proc combineTests(): seq[TestSpec] =
result = @engineTestList
result.add @authTestList
const testList = combineTests()
proc main() = proc main() =
var stat: SimStat var stat: SimStat
let start = getTime() let start = getTime()
for x in engineTestList: for x in testList:
var t = setupELClient(x.chainFile) var t = setupELClient(x.chainFile, x.enableAuth)
t.setRealTTD(x.ttd) t.setRealTTD(x.ttd)
if x.slotsToFinalized != 0: if x.slotsToFinalized != 0:
t.slotsToFinalized(x.slotsToFinalized) t.slotsToFinalized(x.slotsToFinalized)

View File

@ -1,61 +1,18 @@
import import
std/tables, std/tables,
test_env,
stew/byteutils, stew/byteutils,
chronicles, chronicles,
unittest2, unittest2,
nimcrypto, nimcrypto,
chronos, chronos,
./helper, "."/[test_env, helper, types],
../../../nimbus/transaction, ../../../nimbus/transaction,
../../../nimbus/rpc/rpc_types, ../../../nimbus/rpc/rpc_types,
../../../nimbus/merge/mergeutils ../../../nimbus/merge/mergeutils
type
TestSpec* = object
name*: string
run*: proc(t: TestEnv): TestStatus
ttd*: int64
chainFile*: string
slotsToFinalized*: int
slotsToSafe*: int
const const
prevRandaoContractAddr = hexToByteArray[20]("0000000000000000000000000000000000000316") prevRandaoContractAddr = hexToByteArray[20]("0000000000000000000000000000000000000316")
template testCond(expr: untyped) =
if not (expr):
when result is bool:
return false
else:
return TestStatus.Failed
template testCond(expr, body: untyped) =
if not (expr):
body
when result is bool:
return false
else:
return TestStatus.Failed
proc `$`(x: Option[Hash256]): string =
if x.isNone:
"none"
else:
$x.get()
proc `$`(x: Option[BlockHash]): string =
if x.isNone:
"none"
else:
$x.get()
proc `$`(x: Option[PayloadID]): string =
if x.isNone:
"none"
else:
x.get().toHex
proc `==`(a: Option[BlockHash], b: Option[Hash256]): bool = proc `==`(a: Option[BlockHash], b: Option[Hash256]): bool =
if a.isNone and b.isNone: if a.isNone and b.isNone:
return true return true

View File

@ -20,6 +20,7 @@ import
rpc/p2p, rpc/p2p,
rpc/engine_api, rpc/engine_api,
rpc/debug, rpc/debug,
rpc/jwt_auth,
sync/protocol, sync/protocol,
utils/tx_pool utils/tx_pool
], ],
@ -38,14 +39,14 @@ type
EthBlockHeader* = common.BlockHeader EthBlockHeader* = common.BlockHeader
TestEnv* = ref object TestEnv* = ref object
conf: NimbusConf conf*: NimbusConf
ctx: EthContext ctx: EthContext
ethNode: EthereumNode ethNode: EthereumNode
chainDB: BaseChainDB chainDB: BaseChainDB
chainRef: Chain chainRef: Chain
rpcServer: RpcSocketServer rpcServer: RpcHttpServer
sealingEngine: SealingEngineRef sealingEngine: SealingEngineRef
rpcClient*: RpcSocketClient rpcClient*: RpcHttpClient
gHeader*: EthBlockHeader gHeader*: EthBlockHeader
ttd*: DifficultyInt ttd*: DifficultyInt
clMock*: CLMocker clMock*: CLMocker
@ -69,8 +70,9 @@ const
# This is the account that sends vault funding transactions. # This is the account that sends vault funding transactions.
vaultAccountAddr* = hexToByteArray[20]("0xcf49fda3be353c69b41ed96333cd24302da4556f") vaultAccountAddr* = hexToByteArray[20]("0xcf49fda3be353c69b41ed96333cd24302da4556f")
vaultKeyHex = "63b508a03c3b5937ceb903af8b1b0c191012ef6eb7e9c3fb7afa94e5d214d376" vaultKeyHex = "63b508a03c3b5937ceb903af8b1b0c191012ef6eb7e9c3fb7afa94e5d214d376"
jwtSecret = "0x7365637265747365637265747365637265747365637265747365637265747365"
proc setupELClient*(t: TestEnv, chainFile: string) = proc setupELClient*(t: TestEnv, chainFile: string, enableAuth: bool) =
if chainFile.len > 0: if chainFile.len > 0:
# disable clique if we are using PoW chain # disable clique if we are using PoW chain
t.conf.networkParams.config.poaEngine = false t.conf.networkParams.config.poaEngine = false
@ -93,7 +95,18 @@ proc setupELClient*(t: TestEnv, chainFile: string) =
initializeEmptyDb(t.chainDB) initializeEmptyDb(t.chainDB)
let txPool = TxPoolRef.new(t.chainDB, t.conf.engineSigner) let txPool = TxPoolRef.new(t.chainDB, t.conf.engineSigner)
t.rpcServer = newRpcSocketServer(["localhost:" & $t.conf.rpcPort]) var key: JwtSharedKey
let kr = key.fromHex(jwtSecret)
if kr.isErr:
echo "JWT SECRET ERROR: ", kr.error
quit(QuitFailure)
let hooks = if enableAuth:
@[httpJwtAuth(key)]
else:
@[]
t.rpcServer = newRpcHttpServer(["localhost:" & $t.conf.rpcPort], hooks)
t.sealingEngine = SealingEngineRef.new( t.sealingEngine = SealingEngineRef.new(
t.chainRef, t.ctx, t.conf.engineSigner, t.chainRef, t.ctx, t.conf.engineSigner,
txPool, EngineStopped txPool, EngineStopped
@ -107,13 +120,13 @@ proc setupELClient*(t: TestEnv, chainFile: string) =
if chainFile.len > 0: if chainFile.len > 0:
if not importRlpBlock(chainFolder / chainFile, t.chainDB): if not importRlpBlock(chainFolder / chainFile, t.chainDB):
quit(QuitFailure) quit(QuitFailure)
else: elif not enableAuth:
t.sealingEngine.start() t.sealingEngine.start()
t.rpcServer.start() t.rpcServer.start()
t.rpcClient = newRpcSocketClient() t.rpcClient = newRpcHttpClient()
waitFor t.rpcClient.connect("localhost", t.conf.rpcPort) waitFor t.rpcClient.connect("localhost", t.conf.rpcPort, false)
t.gHeader = toGenesisHeader(t.conf.networkParams) t.gHeader = toGenesisHeader(t.conf.networkParams)
let kRes = PrivateKey.fromHex(vaultKeyHex) let kRes = PrivateKey.fromHex(vaultKeyHex)
@ -123,16 +136,16 @@ proc setupELClient*(t: TestEnv, chainFile: string) =
t.vaultKey = kRes.get t.vaultKey = kRes.get
proc setupELClient*(chainFile: string): TestEnv = proc setupELClient*(chainFile: string, enableAuth: bool): TestEnv =
result = TestEnv( result = TestEnv(
conf: makeConfig(@["--engine-signer:658bdf435d810c91414ec09147daa6db62406379", "--custom-network:" & genesisFile]) conf: makeConfig(@["--engine-signer:658bdf435d810c91414ec09147daa6db62406379", "--custom-network:" & genesisFile])
) )
setupELClient(result, chainFile) setupELClient(result, chainFile, enableAuth)
proc stopELClient*(t: TestEnv) = proc stopELClient*(t: TestEnv) =
waitFor t.rpcClient.close() waitFor t.rpcClient.close()
waitFor t.sealingEngine.stop() waitFor t.sealingEngine.stop()
t.rpcServer.stop() #waitFor t.rpcServer.stop()
waitFor t.rpcServer.closeWait() waitFor t.rpcServer.closeWait()
# TTD is the value specified in the TestSpec + Genesis.Difficulty # TTD is the value specified in the TestSpec + Genesis.Difficulty

View File

@ -0,0 +1,51 @@
import
std/options,
test_env,
unittest2,
web3/ethtypes,
../../../nimbus/merge/mergeutils
export ethtypes
type
TestSpec* = object
name*: string
run*: proc(t: TestEnv): TestStatus
ttd*: int64
chainFile*: string
slotsToFinalized*: int
slotsToSafe*: int
enableAuth*: bool
template testCond*(expr: untyped) =
if not (expr):
when result is bool:
return false
else:
return TestStatus.Failed
template testCond*(expr, body: untyped) =
if not (expr):
body
when result is bool:
return false
else:
return TestStatus.Failed
proc `$`*(x: Option[Hash256]): string =
if x.isNone:
"none"
else:
$x.get()
proc `$`*(x: Option[BlockHash]): string =
if x.isNone:
"none"
else:
$x.get()
proc `$`*(x: Option[PayloadID]): string =
if x.isNone:
"none"
else:
x.get().toHex