Skip to main content

Cloud HSM to sign Web3.py Ethereum transactions

Project description

๐Ÿ” web3-google-hsm

Feature Value
Technology Python Hatch project GitHub Actions Pytest
Type Checking Ruff Checked with mypy
CI/CD Release Tests Labeler pre-commit codecov
Docs Docs
Package PyPI - Version PyPI - Python Version PyPI - License
Meta GitHub license GitHub last commit GitHub commit activity GitHub top language

๐ŸŽฏ Description

A Python library for using Google Cloud HSM services to sign Ethereum transactions.

โœจ Features

  • ๐Ÿ”’ Cloud HSM integration for secure key management
  • ๐ŸŒ Support for web3-google-hsm (extensible to other providers)
  • ๐Ÿ›ก๏ธ Type-safe configuration using Pydantic

๐Ÿ“ฆ Installation

  • Install using pip
pip install web3-google-hsm

demo

๐Ÿ› ๏ธ Environment Setup

๐Ÿ”‘ Google Cloud HSM Key

Make sure you have created a key of type ec-sign-secp256k1-sha256 in the Google cloud console. Which will look something like the following

gcp_hsm_key

๐ŸŒŸ Required Environment Variables

Before using this library, you need to set up the following environment variables:

GOOGLE_CLOUD_PROJECT=your-project-id
GOOGLE_CLOUD_REGION=us-east1
KEY_RING=eth-keyring
KEY_NAME=eth-key
GOOGLE_APPLICATION_CREDENTIALS=path/to/your/service-account.json

๐Ÿ’ป Bash

# Add to ~/.bashrc or ~/.bash_profile
export GOOGLE_CLOUD_PROJECT="your-project-id"
export GOOGLE_CLOUD_REGION="us-east1"
export KEY_RING="eth-keyring"
export KEY_NAME="eth-key"
export GOOGLE_APPLICATION_CREDENTIALS="path/to/your/service-account.json"

# Apply changes
source ~/.bashrc  # or source ~/.bash_profile

๐Ÿš Zsh

# Add to ~/.zshrc
export GOOGLE_CLOUD_PROJECT="your-project-id"
export GOOGLE_CLOUD_REGION="us-east1"
export KEY_RING="eth-keyring"
export KEY_NAME="eth-key"
export GOOGLE_APPLICATION_CREDENTIALS="path/to/your/service-account.json"

# Apply changes
source ~/.zshrc

๐ŸŸ Fish

# Add to ~/.config/fish/config.fish
set -x GOOGLE_CLOUD_PROJECT "your-project-id"
set -x GOOGLE_CLOUD_REGION "us-east1"
set -x KEY_RING "eth-keyring"
set -x KEY_NAME "eth-key"
set -x GOOGLE_APPLICATION_CREDENTIALS "path/to/your/service-account.json"
set -x INFURA_KEY "your-infura-key"
set -x WEB3_PROVIDER_URI "https://mainnet.infura.io/v3/$INFURA_KEY"

# Apply changes
source ~/.config/fish/config.fish

๐Ÿ“„ Using .env File

You can also create a .env file in your project root:

# .env
GOOGLE_CLOUD_PROJECT=your-project-id
GOOGLE_CLOUD_REGION=us-east1
KEY_RING=eth-keyring
KEY_NAME=eth-key
GOOGLE_APPLICATION_CREDENTIALS=path/to/your/service-account.json

Then load it in your Python code:

from dotenv import load_dotenv
load_dotenv()

๐Ÿ“‹ Prerequisites

Before you begin, ensure you have:

  1. ๐Ÿ”ง Set up your environment variables (see README.md)
  2. ๐Ÿ Python 3.10 or higher installed
  3. ๐ŸŒ Access to a Web3 provider (local or remote) (Optional)

๐Ÿ“ Environment Variable Descriptions

  • ๐ŸŒ GOOGLE_CLOUD_PROJECT: Your Google Cloud project ID
  • ๐Ÿ“ GOOGLE_CLOUD_REGION: The region where your KMS resources are located (e.g., us-east1, europe-west1)
  • ๐Ÿ”‘ KEY_RING: The name of your KMS key ring
  • ๐Ÿ—๏ธ KEY_NAME: The name of your KMS key
  • ๐Ÿ“œ GOOGLE_APPLICATION_CREDENTIALS: Path to your Google Cloud service account JSON key file

โœ… Verifying Setup

You can verify your environment setup with:

from web3_google_hsm.config import BaseConfig

try:
    config = BaseConfig()
    print("Environment configured successfully!")
    print(f"Project ID: {config.project_id}")
    print(f"Region: {config.location_id}")
except ValueError as e:
    print(f"Configuration error: {e}")

๐Ÿ“š Usage Guide

๐Ÿ“š As A CLI Tool

cli_tool

๐Ÿ”‘ Key Generation

Generate a new Ethereum signing key in Google Cloud HSM:

# Specify explicitly
web3-google-hsm generate \
  --project-id my-project \
  --location us-east1 \
  --keyring eth-keyring \
  --key-id eth-key-1 \
  --retention-days 365
web3-google-hsm generate --project-id hsm-testing-445507 --location nam10 --keyring eth-keyring --key-id cli_key

Options:

  • --project-id: Google Cloud project ID (env: GOOGLE_CLOUD_PROJECT)
  • --location: Cloud KMS location (env: GOOGLE_CLOUD_REGION)
  • --keyring: Name of the key ring (env: KEY_RING)
  • --key-id: ID for the new key (env: KEY_NAME)
  • --retention-days: Days to retain key versions (default: 365)

Example output:

โœ… Created Ethereum signing key: projects/my-project/locations/us-east1/keyRings/eth-keyring/cryptoKeys/eth-key-1
๐Ÿ”‘ Ethereum address: 0x742d35Cc6634C0532925a3b844Bc454e4438f44e

๐Ÿ“ Message Signing

Sign a message using your HSM key:

# Sign a simple message
web3-google-hsm sign "Hello Ethereum!" --account 0x742d35Cc6634C0532925a3b844Bc454e4438f44e

# Sign a hex message
web3-google-hsm sign "0x4d7920686578206d657373616765" --account 0x742d35Cc6634C0532925a3b844Bc454e4438f44e

Arguments:

  • message: The message to sign (text or hex)
  • --account, -a: Ethereum address of the signing account

Example output:

โœ… Message signed successfully!
๐Ÿ“ Message: Hello Ethereum!
๐Ÿ” Signature: 0x4d7920686578206d657373616765000000000000000000000000000000000000
๐Ÿ“Š Components:
  v: 27
  r: 0x1b7e9c7c039d8f4688a743b0c5c0e509209e6f200d956bf7f4e89f5ad330c135
  s: 0x0d27e9c7c039d8f4688a743b0c5c0e509209e6f200d956bf7f4e89f5ad330c13

This guide demonstrates how to use the Google Cloud KMS Ethereum signer library for message and transaction signing.

๐Ÿš€ Basic Setup

from web3_google_hsm.accounts.gcp_kms_account import GCPKmsAccount

account = GCPKmsAccount()

# Get the Ethereum address derived from your GCP KMS key
print(f"GCP KMS Account address: {account.address}")

๐Ÿ“ Message Signing

โœ๏ธ Simple Message Signing

# Sign a message
message = "Hello Ethereum!"
signed_message = account.sign_message(message)

# Access signature components
print(f"R: {signed_message.r.hex()}")
print(f"S: {signed_message.s.hex()}")
print(f"V: {signed_message.v}")
print(f"Full signature: {signed_message.to_hex()}")

โœ”๏ธ Message Signature Verification

from eth_account.messages import encode_defunct
from web3 import Web3
# Create message hash
message_hash = encode_defunct(text=message)

# Initialize Web3 and GCP KMS account
w3 = Web3(Web3.HTTPProvider("http://localhost:8545"))

# Verify the signature using web3.py
recovered_address = w3.eth.account.recover_message(
    message_hash,
    vrs=(signed_message.v, signed_message.r, signed_message.s)
)

# Check if signature is valid
is_valid = recovered_address.lower() == account.address.lower()
print(f"Signature valid: {is_valid}")

๐Ÿ’ณ Transaction Signing

๐Ÿ“ค Creating a Transaction

tx = {
    "from": account.address,
    "chain_id": w3.eth.chain_id,
    "nonce": w3.eth.get_transaction_count(account.address),
    "value": w3.to_wei(0.000001, "ether"),
    "data": "0x00",
    "to": "0xa5D3241A1591061F2a4bB69CA0215F66520E67cf",
    "type": 0,
    "gas_limit": 1000000,
    "gas_price": 300000000000,
}

# Convert dict to Transaction object and sign
signed_tx = account.sign_transaction(Transaction.from_dict(tx))

๐Ÿ“ก Sending a Transaction

if signed_tx:
    # Send the transaction
    tx_hash = w3.eth.send_raw_transaction(signed_tx)

    # Wait for transaction receipt
    receipt = w3.eth.wait_for_transaction_receipt(tx_hash)

    print(f"Transaction hash: {receipt['transactionHash'].hex()}")
    print(f"From: {receipt['from']}")
    print(f"To: {receipt['to']}")
    print(f"Gas used: {receipt['gasUsed']}")

๐Ÿ” Transaction Signature Verification

# Verify the transaction signature
recovered_address = w3.eth.account.recover_transaction(signed_tx)
is_valid = recovered_address.lower() == account.address.lower()
print(f"Signature valid: {is_valid}")

๐Ÿ—๏ธ Working with Local Test Networks

Funding Your Account (for testing with Anvil/Hardhat)

# Use a test account (Anvil's default funded account)
funded_account = w3.eth.account.from_key(
    "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
)

# Create funding transaction
fund_tx = {
    "from": funded_account.address,
    "to": account.address,
    "value": w3.to_wei(0.1, "ether"),
    "gas": 21000,
    "gasPrice": w3.eth.gas_price,
    "nonce": w3.eth.get_transaction_count(funded_account.address),
    "chainId": w3.eth.chain_id,
}

# Send funding transaction
signed_fund_tx = w3.eth.account.sign_transaction(fund_tx, funded_account.key)
fund_tx_hash = w3.eth.send_raw_transaction(signed_fund_tx.raw_transaction)
fund_receipt = w3.eth.wait_for_transaction_receipt(fund_tx_hash)

๐ŸŒ Using with Different Networks

๐Ÿ  Local Network (Anvil/Hardhat)

w3 = Web3(Web3.HTTPProvider("http://localhost:8545"))

๐ŸŒ Mainnet (via Infura)

w3 = Web3(Web3.HTTPProvider(f"https://mainnet.infura.io/v3/{INFURA_KEY}"))

๐Ÿงช Testnet (sepolia)

w3 = Web3(Web3.HTTPProvider(f"https://sepolia.infura.io/v3/{INFURA_KEY}"))

โš ๏ธ Error Handling

from web3_google_hsm.types.ethereum_types import Transaction

try:
    signed_message = account.sign_message("Hello")
except Exception as e:
    print(f"Signing error: {e}")

try:
    signed_tx = account.sign_transaction(Transaction.from_dict(tx))
    if not signed_tx:
        print("Failed to sign transaction")
except Exception as e:
    print(f"Transaction error: {e}")

๐Ÿš€ CI/CD Pipeline Integration

๐Ÿ”„ Using in GitHub Actions or Other CI/CD Pipelines

For CI/CD environments where you can't use traditional environment variables or service account files, you can pass the credentials directly as a JSON string:

from web3_google_hsm.accounts.gcp_kms_account import GCPKmsAccount
from web3_google_hsm.config import BaseConfig
import json

# Create config from environment variables
config = BaseConfig(
    project_id="your-project-id",
    location_id="your-location",
    key_ring_id="your-keyring",
    key_id="your-key-id"
)

# Load credentials from CI/CD secret
credentials = json.loads(os.environ["GCP_ADC_CREDENTIALS_STRING"])

# Initialize account with both config and credentials
account = GCPKmsAccount(config=config, credentials=credentials)

# or Let the class read the values from env variables
account = GCPKmsAccount(credentials=credentials)

๐Ÿ”’ GitHub Actions Example

name: Deploy with HSM Signing

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: '3.10'

      - name: Install dependencies
        run: pip install web3-google-hsm

      - name: Sign and Deploy
        env:
          GOOGLE_CLOUD_PROJECT: ${{ secrets.GCP_PROJECT_ID }}
          GOOGLE_CLOUD_REGION: ${{ secrets.GCP_REGION }}
          KEY_RING: ${{ secrets.GCP_KEYRING }}
          KEY_NAME: ${{ secrets.GCP_KEY_NAME }}
          GCP_ADC_CREDENTIALS_STRING: ${{ secrets.GCP_ADC_CREDENTIALS_STRING }}
        run: |
          python your_deployment_script.py

๐Ÿ“ Example Deployment Script

import os
import json
from web3_google_hsm.accounts.gcp_kms_account import GCPKmsAccount
from web3_google_hsm.config import BaseConfig
from web3_google_hsm.types.ethereum_types import Transaction

def deploy_contract():
    # Initialize with both config and credentials
    config = BaseConfig.from_env()  # Uses environment variables
    credentials = json.loads(os.environ["GCP_ADC_CREDENTIALS_STRING"])

    account = GCPKmsAccount(config=config, credentials=credentials)

    # Your deployment logic here
    print(f"Deploying from address: {account.address}")

    # Example transaction
    tx = Transaction(
        nonce=0,
        gas_price=2000000000,
        gas_limit=1000000,
        to="0x...",
        value=0,
        data="0x...",
        chain_id=1
    )

    signed_tx = account.sign_transaction(tx)
    # Send transaction...

if __name__ == "__main__":
    deploy_contract()

๐Ÿ”‘ Required Secrets for CI/CD

Set these secrets in your CI/CD environment:

  • GCP_PROJECT_ID: Your Google Cloud project ID
  • GCP_REGION: The region where your KMS resources are located
  • GCP_KEYRING: The name of your KMS key ring
  • GCP_KEY_NAME: The name of your KMS key
  • GCP_ADC_CREDENTIALS_STRING: Your service account credentials JSON as a string

For more information see the following links.

๐Ÿ“š Documentation: https://ankvik-tech-labs.github.io/web3-google-hsm/

๐Ÿ’ป Source Code: https://github.com/Ankvik-Tech-Labs/web3-google-hsm


Development

๐Ÿ‘จโ€๐Ÿ’ป Development

๐Ÿ”ง Setup environment

We use Hatch to manage the development environment and production build. Ensure it's installed on your system.

๐Ÿงช Run unit tests

You can run all the tests with:

hatch run test

โœจ Format the code

Execute the following command to apply linting and check typing:

hatch run lint

๐Ÿ“ฆ Publish a new version

You can bump the version, create a commit and associated tag with one command:

hatch version patch
hatch version minor
hatch version major

Your default Git text editor will open so you can add information about the release.

When you push the tag on GitHub, the workflow will automatically publish it on PyPi and a GitHub release will be created as draft.

๐Ÿ“š Serve the documentation

You can serve the Mkdocs documentation with:

hatch run docs-serve

It'll automatically watch for changes in your code.

๐Ÿ“– Further Reading

๐Ÿ“œ License

This project is licensed under the terms of the BSD license.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

web3_google_hsm-0.1.0.tar.gz (2.1 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

web3_google_hsm-0.1.0-py3-none-any.whl (19.7 kB view details)

Uploaded Python 3

File details

Details for the file web3_google_hsm-0.1.0.tar.gz.

File metadata

  • Download URL: web3_google_hsm-0.1.0.tar.gz
  • Upload date:
  • Size: 2.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.0.1 CPython/3.11.2

File hashes

Hashes for web3_google_hsm-0.1.0.tar.gz
Algorithm Hash digest
SHA256 37107455b35888537dd20731d78aefc3137800bb6c0dab4b967f3d76b7fe9d42
MD5 b8d2cb8c0f273a6d8ab83aa43976c910
BLAKE2b-256 3657a73576ece5435faf28b8e352135b6d89d44424b096182c2dc1026875a04c

See more details on using hashes here.

File details

Details for the file web3_google_hsm-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for web3_google_hsm-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 770158608e121cc3caaa51452794f856b3a3ed6d2a740f551c1eab451e7aabb1
MD5 1f0b267983471a92a2383e9600eeb26f
BLAKE2b-256 8ad7b6d4fbafb8a812f69a7887f1da7a344590aa8aea64500094b6f461eed34d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page