Skip to main content

Public Service Staking Signature Structure

To authorize publicServiceStaking operations or their corresponding unstaking actions, the user must generate a cryptographic signature compliant with the EIP-191 standard using the Ethereum Signed Message format.

The signature verification process uses the SignatureRecover library which implements the standard Ethereum message signing protocol. This signature proves the user's intent and authorization to perform a specific staking or unstaking action with a defined amount of staking tokens to a specific service address, according to the parameters provided in the signed message.

Verification Function

The signature is verified using the verifyMessageSignedForPublicServiceStake function:

function verifyMessageSignedForPublicServiceStake(
uint256 evvmID,
address user,
address serviceAddress,
bool _isStaking,
uint256 _amountOfStaking,
uint256 _nonce,
bytes memory signature
) internal pure returns (bool)

Signed Message Format

The signature verification uses the SignatureRecover.signatureVerification function with the following structure:

SignatureRecover.signatureVerification(
Strings.toString(evvmID), // EVVM ID as string
"publicServiceStaking", // Action type
string.concat( // Concatenated parameters
AdvancedStrings.addressToString(serviceAddress),
",",
_isStaking ? "true" : "false",
",",
Strings.toString(_amountOfStaking),
",",
Strings.toString(_nonce)
),
signature,
user
);

Internal Message Construction

Internally, the SignatureRecover.signatureVerification function constructs the final message by concatenating:

string.concat(evvmID, ",", functionName, ",", inputs)

This results in a message format:

"{evvmID},publicServiceStaking,{serviceAddress},{isStaking},{amountOfStaking},{nonce}"

EIP-191 Message Hashing

The message is then hashed according to EIP-191 standard:

bytes32 messageHash = keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n",
Strings.toString(bytes(message).length),
message
)
);

Message Components

The signature verification takes three main parameters:

1. EVVM ID (String):

  • The result of Strings.toString(evvmID)
  • Purpose: Identifies the specific EVVM instance

2. Action Type (String):

  • Fixed value: "publicServiceStaking"
  • Purpose: Identifies this as a public service staking operation

3. Concatenated Parameters (String): The parameters are concatenated with comma separators:

3.1. Service Address (String):

  • The result of AdvancedStrings.addressToString(serviceAddress)
  • Purpose: The lowercase string representation of the service contract address that will receive the staked tokens

3.2. Staking Action Flag (String):

  • "true": If the user intends to stake (_isStaking is true)
  • "false": If the user intends to unstake (_isStaking is false)
  • Purpose: Indicates whether the operation is staking or unstaking

3.3. Staking Amount (String):

  • The result of Strings.toString(_amountOfStaking)
  • Purpose: The quantity of staking tokens for this operation

3.4. Nonce (String):

  • The result of Strings.toString(_nonce)
  • Purpose: Provides replay protection for public service staking operations

Examples

Public Service Staking Example

Scenario: User wants to stake 2000 tokens to a specific service

Parameters:

  • evvmID: 1
  • serviceAddress: 0x1234567890abcdef1234567890abcdef12345678
  • _isStaking: true (staking operation)
  • _amountOfStaking: 2000
  • _nonce: 15

Signature verification call:

SignatureRecover.signatureVerification(
"1", // evvmID as string
"publicServiceStaking", // action type
"0x1234567890abcdef1234567890abcdef12345678,true,2000,15", // concatenated parameters
signature,
user
);

Final message to be signed:

1,publicServiceStaking,0x1234567890abcdef1234567890abcdef12345678,true,2000,15

EIP-191 formatted message hash:

keccak256(abi.encodePacked(
"\x19Ethereum Signed Message:\n85",
"1,publicServiceStaking,0x1234567890abcdef1234567890abcdef12345678,true,2000,15"
))

Public Service Unstaking Example

Scenario: User wants to unstake 1500 tokens from a specific service

Parameters:

  • evvmID: 1
  • serviceAddress: 0xabcdef1234567890abcdef1234567890abcdef12
  • _isStaking: false (unstaking operation)
  • _amountOfStaking: 1500
  • _nonce: 16

Signature verification call:

SignatureRecover.signatureVerification(
"1", // evvmID as string
"publicServiceStaking", // action type
"0xabcdef1234567890abcdef1234567890abcdef12,false,1500,16", // concatenated parameters
signature,
user
);

Final message to be signed:

1,publicServiceStaking,0xabcdef1234567890abcdef1234567890abcdef12,false,1500,16

EIP-191 formatted message hash:

keccak256(abi.encodePacked(
"\x19Ethereum Signed Message:\n82",
"1,publicServiceStaking,0xabcdef1234567890abcdef1234567890abcdef12,false,1500,16"
))

Signature Implementation Details

The SignatureRecover library performs signature verification in the following steps:

  1. Message Construction: Concatenates evvmID, functionName, and inputs with commas
  2. EIP-191 Formatting: Prepends "\x19Ethereum Signed Message:\n" + message length
  3. Hashing: Applies keccak256 to the formatted message
  4. Signature Parsing: Splits the 65-byte signature into r, s, and v components
  5. Recovery: Uses ecrecover to recover the signer's address
  6. Verification: Compares recovered address with expected user

Signature Format Requirements

  • Length: Exactly 65 bytes
  • Structure: [r (32 bytes)][s (32 bytes)][v (1 byte)]
  • V Value: Must be 27 or 28 (automatically adjusted if < 27)
Technical Details
  • Message Format: The final message follows the pattern "{evvmID},{functionName},{parameters}"
  • EIP-191 Compliance: Uses "\x19Ethereum Signed Message:\n" prefix with message length
  • Hash Function: keccak256 is used for the final message hash before signing
  • Signature Recovery: Uses ecrecover to verify the signature against the expected user
  • Service-Specific: Service address is included to prevent cross-service replay attacks
  • Dual Operations: Single function handles both staking (true) and unstaking (false)
  • Address Format: AdvancedStrings.addressToString converts addresses to lowercase hex with "0x" prefix
  • Nonce Management: Each nonce should be unique to prevent replay attacks
  • EVVM ID: Identifies the specific EVVM instance for signature verification