Skip to content

Commit

Permalink
Add getsignatures test (#60)
Browse files Browse the repository at this point in the history
* add getsignatures test

* get current dir

* add token.jwt
  • Loading branch information
Marketen authored Jun 11, 2024
1 parent f1f1c3c commit 916e31c
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 73 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
.env
./listener/tmp/*
./listener/bin/*
private.pem
public.pem

2 changes: 1 addition & 1 deletion ci.env
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ BEACON_NODE_URL_MAINNET=http://172.33.0.27:3500
BEACON_NODE_URL_HOLESKY=http://172.33.0.27:3500
BEACON_NODE_URL_GNOSIS=http://172.33.0.27:3500
BEACON_NODE_URL_LUKSO=http://172.33.0.27:3500
JWT_USERS_FILE=users.json.example
JWT_USERS_FILE=users-ci.json
6 changes: 6 additions & 0 deletions jwt/users-ci.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"stader": {
"publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuGqOvmSNI7qMrNa1CjRK\nxmp+M9vR6AJduw//e69LwFtt2Zv0nrn/06H+NdjEuuKJAn7XaCjk1n59SxZCpUAX\n1eyy7oV/IVrdmt7yupOasXLcKbSoqov9sxvLkgNrmdZxEpQKTpMlu55srX72Ioyo\nHYONkw6PunxRPhYKCTc4YKwgCYCfOKxBk/lilWuYmH4nlQ533TOiMfiJmw+WTPCA\n9sqm9ID84FDKk0/xeukQznRoFB5ACpk2J9c7r7IoRnL5gaRGjLQ76kFzQ08ZqcHX\n9Fbkop88ktV95YqHvsL3NwkdQfrR6BtJTaQQ9lPfyYo5O37MOCE/N+RoldToL2xa\nZwIDAQAB\n-----END PUBLIC KEY-----",
"tags": ["solo"]
}
}
1 change: 1 addition & 0 deletions listener/cmd/jwt-generator/token.jwt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eyJhbGciOiJSUzI1NiIsImtpZCI6InlvIiwidHlwIjoiSldUIn0.e30.R1e1YWwi5nacrd0fZ3Uoj6o-sTGsrXO8oojWH6HOvD9gJXNj7qNBIR16o6vR2DQAJKPNxh5nhuyFZg8kiwvjG2WCpEBPxqvklbXlGWZqtbD57fOIu7CCAyhBvIh3TkmmAiZcGFkXwjKW62FE0_JKLtBWVBFR2hROJ27oHgW3AGtK13eZvtV8P9qpB4uOYx7GZCZxzPy7zIV-SZ3GNF7kHuosfJxruMMwsk4wZiZsfVGjIPQ4EVJFX8KDw0FzmdKXwsaSsehDqopGmEgL1UDpIDsscg3sX51GvzaUfZkK0hX7iw_-q4Zvux4nLhBo8QkO8Gc2enfVWelv_2oKkLBlvA
27 changes: 27 additions & 0 deletions listener/test/data/private.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAuGqOvmSNI7qMrNa1CjRKxmp+M9vR6AJduw//e69LwFtt2Zv0
nrn/06H+NdjEuuKJAn7XaCjk1n59SxZCpUAX1eyy7oV/IVrdmt7yupOasXLcKbSo
qov9sxvLkgNrmdZxEpQKTpMlu55srX72IoyoHYONkw6PunxRPhYKCTc4YKwgCYCf
OKxBk/lilWuYmH4nlQ533TOiMfiJmw+WTPCA9sqm9ID84FDKk0/xeukQznRoFB5A
Cpk2J9c7r7IoRnL5gaRGjLQ76kFzQ08ZqcHX9Fbkop88ktV95YqHvsL3NwkdQfrR
6BtJTaQQ9lPfyYo5O37MOCE/N+RoldToL2xaZwIDAQABAoIBAQCA98Qr0hYCTovi
s6SYJknEyOTJT4lUP5NdJqr8zHKla0s/S5mfB2SplDg7YAT0zUgTvrDV3wNqHbtx
r3ecKV6B9K6NsD5gDTO9Av2tDVy5jCH76KOr4YrinyWWT14Ij7czzuQGX1GcDAbk
rk1jjApl2YJAvYY2XIpUaQVLvJpov8cVrmAuBwUNzbsd35w+d0rxpaz94nmycQZt
BHPF8BvPpBa6TaS/maJWjZlvRGHdD/9F5GpJD01tSfwstw3lDG6jlKDevjeEpsnB
Of/B8lRSJd5lrHLDX/wFqY1YxxqvEkd1nZUNYlSr6L35MsLWaswFZa1gEep6tkEv
jvdsq9pBAoGBANzcoXZ4/W8Z4qi80auaaRf0uZiIniqbDey0iyYkpsmZ06EOeTMR
o0xynYmGOvF9pn/heDY+1oM+/gMGI+rDmB83I2mbp9hfv7nIq22NOerXJhSnJXve
sFn7VAmOnNrGGQLQ3oMxSC3nJcFzLsEkX3yJkL3sAMTd3DUY7y1AdLLLAoGBANXB
jXyv6eDiGYk1cPuGnqNZdS/sEZqvNbkya8SCWeHnlAKlQe2/2fGA0IYXR0vE4qlL
EhXf/nN52ruB0AvuwYpZf894/C2LrZHBCrXUqno0PGH+L3vjzv248F2OGK+wLx4E
aJ1MY3j6/vKw2oScEH5QVBnLQmybiTq9MrpgfldVAoGAa7n5z22IQA6iLaebpVX1
eMfXVv0cGK/0hMYzMPGjoKW1QdNrbmtl+T3WdWPRkES3V5zEI8FWpEGvaA4wqquo
oWEllrdjkPhy7c1hQFgoLdGvM1erwtWFsv4RqW+0Nkl1nZf/UIJTMICUO91Qqshx
Aq+et+RLI7sLU6LL1oif4y0CgYB3HgIjjsBNYpIKZS6N7CnxK4PjbbEtux+8EX3+
pwlBm1Re2QrRW40vSLJrVwOTFKee4ce1Svq4DRq4TRHL5IQT/eX8jxYwp1rVE3dN
drJ9oShZD1YUuxF5UJsZ93qIRS0slBZcOdpg67YxNh6/sVx7l3YWXa+paE9j3VUs
iWM+dQKBgQCMhqECHjlcRTm7AyHYSrZ0T9qnOGrqBImFYb8niTVVciDQKpMtRQxs
7uCus+9dV6y8NerIsUfiWrUhLmYXeOPOKoV4Fvykg7IdUv5515LN2D7FJukYQL2a
OAtuwY2bjDvXbjf88a2MsAeg1iwaueb8GpjU5XTKqZMN2JJy5Z9wlw==
-----END RSA PRIVATE KEY-----
9 changes: 9 additions & 0 deletions listener/test/data/public.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuGqOvmSNI7qMrNa1CjRK
xmp+M9vR6AJduw//e69LwFtt2Zv0nrn/06H+NdjEuuKJAn7XaCjk1n59SxZCpUAX
1eyy7oV/IVrdmt7yupOasXLcKbSoqov9sxvLkgNrmdZxEpQKTpMlu55srX72Ioyo
HYONkw6PunxRPhYKCTc4YKwgCYCfOKxBk/lilWuYmH4nlQ533TOiMfiJmw+WTPCA
9sqm9ID84FDKk0/xeukQznRoFB5ACpk2J9c7r7IoRnL5gaRGjLQ76kFzQ08ZqcHX
9Fbkop88ktV95YqHvsL3NwkdQfrR6BtJTaQQ9lPfyYo5O37MOCE/N+RoldToL2xa
ZwIDAQAB
-----END PUBLIC KEY-----
1 change: 1 addition & 0 deletions listener/test/data/token.jwt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eyJhbGciOiJSUzI1NiIsImtpZCI6InN0YWRlciIsInR5cCI6IkpXVCJ9.e30.AbouFU5vgAAdBaFMTclV3ddiZTf2U9xKi-qsX4PESAfGwI4e_mTrzP9F43j45vemU8yBMFOL6pwouMK70lbumfb0k1Htlx_xy2HSHNZsxLDvCR1NR0p_s8wCsIe4xuSpBtGdcgabqNFLxvaUrg0h0k42JewhguKlbCnPVz6LQpE-V9YTxZMHpxn2hevRAq7mQVXPHsXH-08QqUB1AwVwnWzeoBxMSCcs9VsiNrbxeZxqmZDHBaF5FJjnbZm1nZLb_OWm73tpDvWCuOx0ie0tW_jUbW3-tlsOSPU2tcRRNmvWuuuRMkrduTu9NGeJOmTauOkUKJ6ZoiVI76-qu1Lkqw
234 changes: 164 additions & 70 deletions listener/test/sendSignatures_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/base64"
"encoding/json"
"net/http"
"os"
"strconv"
"testing"
"time"
Expand All @@ -13,118 +14,211 @@ import (
"github.com/herumi/bls-eth-go-binary/bls"
)

// TestPostSignaturesIntegration tests the POST /signatures endpoint. It expects a "listener" service to be running at
// http://localhost:8080, with the proper mongoDB connected to it. The test sends a series of requests with different payloads,
// public keys, signatures, and tags to the endpoint and checks the response status code.
func TestPostSignaturesIntegration(t *testing.T) {
// Initialize BLS for the test
if err := bls.Init(bls.BLS12_381); err != nil {
t.Fatalf("Failed to initialize BLS: %v", err)
func readJWT(filePath string) (string, error) {
data, err := os.ReadFile(filePath)
if err != nil {
return "", err
}
return string(data), nil
}

// Create a new HTTPExpect instance
e := httpexpect.Default(t, "http://localhost:8080")
// generateSignature generates a BLS signature and base64 encoded payload using a secret key.
func generateSignature(payload types.DecodedPayload, secretKey *bls.SecretKey) (signatureHex, payloadBase64 string) {
payloadBytes, _ := json.Marshal(payload)
signature := secretKey.SignByte(payloadBytes)
signatureHex = "0x" + signature.SerializeToHexStr()
payloadBase64 = base64.StdEncoding.EncodeToString(payloadBytes)
return
}

// Generate valid BLS keys and signature
func setupSecretKey() *bls.SecretKey {
var secretKey bls.SecretKey
secretKey.SetByCSPRNG()
publicKey := secretKey.GetPublicKey()
publicKeyHex := "0x" + publicKey.SerializeToHexStr()

// Prepare timestamps and payloads
currentTime := time.Now()
validTimestamp := currentTime.AddDate(0, 0, -10).UnixMilli() // timestamp is 10 days ago
return &secretKey
}

validDecodedPayload := types.DecodedPayload{
Type: "PROOF_OF_VALIDATION",
Platform: "dappnode",
Timestamp: strconv.FormatInt(validTimestamp, 10),
func TestPostSignaturesIntegration(t *testing.T) {
// BLS initialization
if err := bls.Init(bls.BLS12_381); err != nil {
t.Fatalf("Failed to initialize BLS: %v", err)
}
payloadBytes, _ := json.Marshal(validDecodedPayload)
validPayload := base64.StdEncoding.EncodeToString(payloadBytes)
signature := secretKey.SignByte(payloadBytes)
validSignature := "0x" + signature.SerializeToHexStr()
invalidDecodedPayload := types.DecodedPayload{
Type: "INVALID_TYPE",
Platform: "dappnode",
Timestamp: strconv.FormatInt(validTimestamp, 10),
e := httpexpect.Default(t, "http://localhost:8080")

// Setup test data
currentTime := time.Now().UnixMilli()
secretKey := setupSecretKey()
secretKey2 := setupSecretKey()

// Read JWT token from file
jwtToken, err := readJWT("data/token.jwt")
if err != nil {
t.Fatalf("Failed to read JWT: %v", err)
}
invalidPayloadBytes, _ := json.Marshal(invalidDecodedPayload)
invalidPayload := base64.StdEncoding.EncodeToString(invalidPayloadBytes)

// Define test cases
// TODO: we should add the expected message for each case too, besides the expected code
tests := []struct {
testCases := []struct {
description string
payload string
pubkey string
signature string
tag types.Tag
payload types.DecodedPayload
secretKey *bls.SecretKey
expectedCode int
tag types.Tag
invalidKey bool // Use invalid key if true
customSig string // Use custom signature if not empty
}{
{
description: "Valid request",
payload: validPayload,
pubkey: publicKeyHex,
signature: validSignature,
tag: types.Solo,
description: "Valid request solo tag",
payload: types.DecodedPayload{
Type: "PROOF_OF_VALIDATION",
Platform: "dappnode",
Timestamp: strconv.FormatInt(currentTime-10*24*60*60*1000, 10), // 10 days ago
},
secretKey: secretKey,
expectedCode: http.StatusOK,
tag: types.Solo,
},
{
description: "Invalid payload format",
payload: invalidPayload,
pubkey: publicKeyHex,
signature: validSignature,
description: "Valid request solo tag with new timestamp",
payload: types.DecodedPayload{
Type: "PROOF_OF_VALIDATION",
Platform: "dappnode",
Timestamp: strconv.FormatInt(currentTime-5*24*60*60*1000, 10), // 5 days ago
},
secretKey: secretKey, // Reuse the key for a different payload
expectedCode: http.StatusOK,
tag: types.Solo,
expectedCode: http.StatusBadRequest,
},
{
description: "Valid signature format arbitrary bytes signed, shouldnt pass the crypto verification",
payload: validPayload,
pubkey: publicKeyHex,
signature: "0x8bc341f083e34d27b8df9f48b0bfcdaa7ed009146969cee0d0d4e03afd383242e1767627d5e2ef50cce410dd02ed88280bb91309f96e5ad1ad31b204f1ed5e64a43cdf3c32603450b477a40df366f3ae145014cade0f22d588786f4f07bc7c7d",
description: "Valid request solo tag with new timestamp and pubkey",
payload: types.DecodedPayload{
Type: "PROOF_OF_VALIDATION",
Platform: "dappnode",
Timestamp: strconv.FormatInt(currentTime-5*24*60*60*1000, 10), // 5 days ago
},
secretKey: secretKey2, // Reuse the key for a different payload
expectedCode: http.StatusOK,
tag: types.Solo,
expectedCode: http.StatusBadRequest,
},
{
description: "Invalid BLS public key",
payload: validPayload,
pubkey: "0xinvalidKey",
signature: validSignature,
description: "Valid request ssv tag",
payload: types.DecodedPayload{
Type: "PROOF_OF_VALIDATION",
Platform: "dappnode",
Timestamp: strconv.FormatInt(currentTime-10*24*60*60*1000, 10),
},
secretKey: secretKey,
expectedCode: http.StatusOK,
tag: types.Ssv,
},
{
description: "Invalid payload format",
payload: types.DecodedPayload{
Type: "INVALID_TYPE",
Platform: "dappnode",
Timestamp: strconv.FormatInt(currentTime-10*24*60*60*1000, 10),
},
secretKey: secretKey,
expectedCode: http.StatusBadRequest,
tag: types.Solo,
},
{
description: "Valid signature format arbitrary bytes signed, shouldn't pass the crypto verification",
payload: types.DecodedPayload{
Type: "PROOF_OF_VALIDATION",
Platform: "dappnode",
Timestamp: strconv.FormatInt(currentTime-10*24*60*60*1000, 10),
},
secretKey: secretKey,
expectedCode: http.StatusBadRequest,
tag: types.Solo,
customSig: "0x8bc341f083e34d27b8df9f48b0bfcdaa7ed009146969cee0d0d4e03afd383242e1767627d5e2ef50cce410dd02ed88280bb91309f96e5ad1ad31b204f1ed5e64a43cdf3c32603450b477a40df366f3ae145014cade0f22d588786f4f07bc7c7d",
},
{
description: "Invalid JSON format",
payload: `{bad json}`,
pubkey: publicKeyHex,
signature: validSignature,
description: "Invalid BLS public key",
payload: types.DecodedPayload{
Type: "PROOF_OF_VALIDATION",
Platform: "dappnode",
Timestamp: strconv.FormatInt(currentTime-10*24*60*60*1000, 10),
},
secretKey: secretKey,
expectedCode: http.StatusBadRequest,
tag: types.Solo,
invalidKey: true, // Use an invalid key in test
},
{
description: "Invalid JSON format",
payload: types.DecodedPayload{
Type: "INVALID_JSON",
Platform: "dappnode",
Timestamp: `{bad json}`,
},
secretKey: secretKey,
expectedCode: http.StatusBadRequest,
tag: types.Solo,
},
{
description: "Invalid tag",
payload: validPayload,
pubkey: publicKeyHex,
signature: validSignature,
tag: "invalidTag",
description: "Invalid tag",
payload: types.DecodedPayload{
Type: "PROOF_OF_VALIDATION",
Platform: "dappnode",
Timestamp: strconv.FormatInt(currentTime-10*24*60*60*1000, 10),
},
secretKey: secretKey,
expectedCode: http.StatusBadRequest,
tag: "invalidTag",
},
}

// Execute tests
for _, tc := range tests {
// wait some seconds to ensure the server is ready
time.Sleep(10 * time.Second)

// Execute test cases
for _, tc := range testCases {
sigHex, payloadBase64 := generateSignature(tc.payload, tc.secretKey)
if tc.customSig != "" {
sigHex = tc.customSig
}
pubKeyHex := "0x" + tc.secretKey.GetPublicKey().SerializeToHexStr()
if tc.invalidKey {
pubKeyHex = "0xinvalidKey"
}

t.Run(tc.description, func(t *testing.T) {
e.POST("/signatures").
WithQuery("network", "mainnet").
WithJSON([]types.SignatureRequest{{
Payload: tc.payload,
Pubkey: tc.pubkey,
Signature: tc.signature,
Payload: payloadBase64,
Pubkey: pubKeyHex,
Signature: sigHex,
Tag: tc.tag,
}}).
Expect().
Status(tc.expectedCode)
})
}

// wait some seconds to ensure all the signatures are processed
time.Sleep(15 * time.Second)

t.Run("Fetch Signatures with JWT", func(t *testing.T) {
response := e.GET("/signatures").
WithHeader("Authorization", "Bearer "+jwtToken).
Expect().
Status(http.StatusOK).
JSON().Array()

// // Convert the response to a pretty JSON format. Useful for debugging
// jsonResponse, err := json.MarshalIndent(response.Raw(), "", " ")
// if err != nil {
// t.Fatalf("Failed to marshal JSON: %v", err)
// }
// fmt.Printf("Response: %s\n", string(jsonResponse))

// Verify that the response contains two elements
response.Length().IsEqual(2)

// Verify that the first element has pubkey corresponding to secretKey
response.Value(0).Object().Value("pubkey").String().IsEqual("0x" + secretKey.GetPublicKey().SerializeToHexStr())

// Verify that the second element has pubkey corresponding to secretKey2
response.Value(1).Object().Value("pubkey").String().IsEqual("0x" + secretKey2.GetPublicKey().SerializeToHexStr())
})
}

0 comments on commit 916e31c

Please sign in to comment.