Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions add-to-keplr.html
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,9 @@ <h3>Custom Network Guide:</h3>
displayDenom: "shm",
decimals: 18,
bech32Prefix: "shardeum",
rpcUrl: "http://127.0.0.1:26657",
restUrl: "http://127.0.0.1:1317",
jsonRpcUrl: "http://127.0.0.1:8545",
rpcUrl: "http://104.197.79.45:26657",
restUrl: "http://104.197.79.45:1317",
jsonRpcUrl: "http://104.197.79.45:8545",
displayName: "Shardeum Testnet"
},
devnet: {
Expand Down
221 changes: 221 additions & 0 deletions scripts/generate_ethereum_address.py
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unnecessary. The binary already provides a way to do this

shardeumd debug addr shardeum1mrdxhunfvjhe6lhdncp72dq46da2jcz944xc34

shardeumd debug addr d8dA6BF26964aF9D7eEd9e03E53415D37aA96045

Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
#!/usr/bin/env python3
"""
Convert Shardeum bech32 address to Ethereum hex address

This script converts Shardeum bech32 addresses (format: shardeum1...) to their
corresponding Ethereum hex addresses (format: 0x...). This is useful for:
- Interacting with EVM-compatible contracts on Shardeum
- Making JSON-RPC calls that require hex addresses
- Converting between Cosmos SDK and Ethereum address formats

Usage:
python generate_ethereum_address.py <shardeum_bech32_address>

Example:
python generate_ethereum_address.py shardeum1zkwykx6dwzhkv4hspyu9pns34sx03u702a4d7k
Output: 0x159c4b1b4d70af6656f0093850ce11ac0cf8f3cf

The conversion uses the same bech32 encoding/decoding logic as the Shardeum
genesis importer to ensure consistency across the ecosystem.
"""

import sys

# Bech32 encoding/decoding constants
# These are the standard bech32 character set and generator polynomial
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" # Base32 character set for bech32
GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] # Generator polynomial coefficients

def bech32_polymod(values):
"""
Calculate bech32 polymod checksum

This function implements the polymod checksum algorithm used in bech32 encoding.
It's used to verify the integrity of bech32 addresses and generate checksums.

Args:
values: List of 5-bit values to process

Returns:
int: The polymod checksum value
"""
chk = 1 # Initialize checksum accumulator
for value in values:
top = chk >> 25 # Extract top 5 bits
chk = (chk & 0x1ffffff) << 5 ^ value # Shift left 5 bits and XOR with value
for i in range(5): # Apply generator polynomial
chk ^= GEN[i] if ((top >> i) & 1) else 0
return chk

def convertbits(data, frombits, tobits, pad=True):
"""
Convert between different bit group sizes

This function converts data from one bit group size to another. For example,
converting from 5-bit groups (used in bech32) to 8-bit groups (used in hex).

Args:
data: List of values in the source bit group size
frombits: Source bit group size (e.g., 5 for bech32)
tobits: Target bit group size (e.g., 8 for hex)
pad: Whether to pad incomplete groups

Returns:
List of values in the target bit group size, or None if conversion fails
"""
acc = 0 # Accumulator for bits
bits = 0 # Number of bits currently in accumulator
ret = [] # Result list
maxv = (1 << tobits) - 1 # Maximum value for target bit group
max_acc = (1 << (frombits + tobits - 1)) - 1 # Maximum accumulator value

for value in data:
# Validate input value
if value < 0 or (value >> frombits):
return None

# Add new bits to accumulator
acc = ((acc << frombits) | value) & max_acc
bits += frombits

# Extract complete groups
while bits >= tobits:
bits -= tobits
ret.append((acc >> bits) & maxv)

# Handle remaining bits
if pad:
if bits:
ret.append((acc << (tobits - bits)) & maxv)
elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
return None

return ret

def bech32_decode(bech):
"""
Decode a bech32 string into human-readable part and data

This function parses a bech32 address and extracts the human-readable part (HRP)
and the data portion, while validating the format and checksum.

Args:
bech: The bech32 string to decode (e.g., "shardeum1abc...")

Returns:
Tuple of (hrp, data) if successful, (None, None) if failed
"""
# Validate character set and case consistency
if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or
(bech.lower() != bech and bech.upper() != bech)):
return (None, None)

bech = bech.lower() # Convert to lowercase for processing
pos = bech.rfind('1') # Find the separator character '1'

# Validate separator position and minimum length
if pos < 1 or pos + 7 > len(bech) or pos + 1 + 6 > len(bech):
return (None, None)

# Validate that all characters after separator are in the charset
if not all(x in CHARSET for x in bech[pos+1:]):
return (None, None)

# Extract human-readable part and data
hrp = bech[:pos] # Everything before the '1'
data = [CHARSET.find(x) for x in bech[pos+1:]] # Convert characters to 5-bit values

# Verify checksum
if not bech32_verify_checksum(hrp, data):
return (None, None)

# Return HRP and data without checksum (last 6 values)
return (hrp, data[:-6])

def bech32_verify_checksum(hrp, data):
"""
Verify a bech32 string checksum

This function verifies that the checksum in a bech32 address is valid
by computing the polymod of the HRP + data and checking if it equals 1.

Args:
hrp: Human-readable part (e.g., "shardeum")
data: Data portion including checksum

Returns:
bool: True if checksum is valid, False otherwise
"""
return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1

def bech32_hrp_expand(hrp):
"""
Expand the HRP into values for checksum computation

This function converts the human-readable part into a list of 5-bit values
that can be used in the polymod checksum calculation.

Args:
hrp: Human-readable part (e.g., "shardeum")

Returns:
List of 5-bit values representing the HRP
"""
return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]

def bech32_to_hex(bech32_addr):
"""
Convert bech32 address to Ethereum hex address

This is the main conversion function that takes a Shardeum bech32 address
and converts it to the corresponding Ethereum hex address format.

Args:
bech32_addr: Shardeum bech32 address (e.g., "shardeum1abc...")

Returns:
str: Ethereum hex address (e.g., "0x123...") or None if conversion fails
"""
try:
# Decode the bech32 address
hrp, data = bech32_decode(bech32_addr)
if hrp is None or data is None:
return None

# Convert from 5-bit groups (bech32) to 8-bit groups (hex)
conv = convertbits(data, 5, 8, False)
if conv is None:
return None

# Convert bytes to hex string with 0x prefix
return '0x' + ''.join(f'{b:02x}' for b in conv)
except Exception as e:
print(f"Error decoding address {bech32_addr}: {e}", file=sys.stderr)
return None

if __name__ == "__main__":
"""
Main execution block - handles command line arguments and performs conversion
"""
# Validate command line arguments
if len(sys.argv) != 2:
print("Usage: python generate_ethereum_address.py <shardeum_bech32_address>", file=sys.stderr)
print("", file=sys.stderr)
print("Example:", file=sys.stderr)
print(" python generate_ethereum_address.py shardeum1zkwykx6dwzhkv4hspyu9pns34sx03u702a4d7k", file=sys.stderr)
print(" Output: 0x159c4b1b4d70af6656f0093850ce11ac0cf8f3cf", file=sys.stderr)
sys.exit(1)

# Get the bech32 address from command line
bech32_addr = sys.argv[1]

# Perform the conversion
hex_addr = bech32_to_hex(bech32_addr)

# Output the result
if hex_addr:
print(hex_addr)
else:
print("Failed to convert address", file=sys.stderr)
print("Please ensure the address is a valid Shardeum bech32 address", file=sys.stderr)
sys.exit(1)
75 changes: 67 additions & 8 deletions scripts/genesis_account_split.sh
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,35 @@ find_network_genesis() {
return 0
}

# -----------------------------
# OS detection and path handling
# -----------------------------
is_windows() {
case "$(uname -s)" in
CYGWIN*|MINGW*|MSYS*)
return 0
;;
*)
return 1
;;
esac
}

# Convert paths for Python based on OS
convert_path_for_python() {
local path="$1"
if is_windows; then
# Windows: Handle Git Bash mount points and convert backslashes
# Convert /c/ to C:/ for Git Bash compatibility
path=$(echo "$path" | sed 's|^/c/|C:/|' | sed 's|^/d/|D:/|' | sed 's|^/e/|E:/|' | sed 's|^/f/|F:/|' | sed 's|^/g/|G:/|' | sed 's|^/h/|H:/|' | sed 's|^/i/|I:/|' | sed 's|^/j/|J:/|')
# Convert remaining backslashes to forward slashes
echo "$path" | sed 's|\\\\|/|g' | sed 's|\\|/|g'
else
# Unix/Linux: Use as-is
echo "$path"
fi
}

# -----------------------------
# Python implementation embedded
# -----------------------------
Expand Down Expand Up @@ -214,13 +243,18 @@ cmd_split() {
echo ""
print_info "Splitting genesis..."

# Convert paths for Python
local PYTHON_GENESIS_FILE=$(convert_path_for_python "$GENESIS_FILE")
local PYTHON_OUTPUT_DIR=$(convert_path_for_python "$OUTPUT_DIR")

run_python_cmd "
import json
import os
from pathlib import Path

genesis_path = Path('$GENESIS_FILE')
output_dir = Path('$OUTPUT_DIR')
# Use OS-appropriate paths
genesis_path = Path('$PYTHON_GENESIS_FILE')
output_dir = Path('$PYTHON_OUTPUT_DIR')
accounts_per_file = $ACCOUNTS_PER_FILE

# Load genesis file
Expand Down Expand Up @@ -351,14 +385,20 @@ cmd_merge() {

print_info "Merging accounts..."

# Convert paths for Python
local PYTHON_GENESIS_FILE=$(convert_path_for_python "$GENESIS_FILE")
local PYTHON_ACCOUNTS_DIR=$(convert_path_for_python "$ACCOUNTS_DIR")
local PYTHON_OUTPUT=$(convert_path_for_python "$OUTPUT")

run_python_cmd "
import json
import glob
from pathlib import Path

genesis_path = Path('$GENESIS_FILE')
accounts_dir = Path('$ACCOUNTS_DIR')
output_path = Path('$OUTPUT')
# Use OS-appropriate paths
genesis_path = Path('$PYTHON_GENESIS_FILE')
accounts_dir = Path('$PYTHON_ACCOUNTS_DIR')
output_path = Path('$PYTHON_OUTPUT')

# Load main genesis
with open(genesis_path, 'r') as f:
Expand Down Expand Up @@ -704,15 +744,34 @@ load_genesis_with_accounts() {
OUTPUT_PATH="/tmp/merged_genesis_$$.json"
fi

# Convert paths for Python
local PYTHON_GENESIS_PATH=$(convert_path_for_python "$GENESIS_PATH")
local PYTHON_GENESIS_DIR=$(convert_path_for_python "$GENESIS_DIR")
local PYTHON_OUTPUT_PATH=$(convert_path_for_python "$OUTPUT_PATH")

# Debug: Show converted paths
echo "Debug: Original GENESIS_PATH: $GENESIS_PATH" >&2
echo "Debug: Converted PYTHON_GENESIS_PATH: $PYTHON_GENESIS_PATH" >&2
echo "Debug: Original GENESIS_DIR: $GENESIS_DIR" >&2
echo "Debug: Converted PYTHON_GENESIS_DIR: $PYTHON_GENESIS_DIR" >&2

# Merge using embedded Python
python3 -c "
import json
import glob
import os
from pathlib import Path

genesis_path = Path('$GENESIS_PATH')
accounts_dir = Path('$GENESIS_DIR')
output_path = Path('$OUTPUT_PATH')
# Use OS-appropriate paths
genesis_path = Path('$PYTHON_GENESIS_PATH')
accounts_dir = Path('$PYTHON_GENESIS_DIR')
output_path = Path('$PYTHON_OUTPUT_PATH')

# Debug: Print the paths
print(f'Debug: genesis_path: {genesis_path}', file=os.sys.stderr)
print(f'Debug: accounts_dir: {accounts_dir}', file=os.sys.stderr)
print(f'Debug: output_path: {output_path}', file=os.sys.stderr)
print(f'Debug: Genesis file exists: {genesis_path.exists()}', file=os.sys.stderr)

# Load main genesis
with open(genesis_path, 'r') as f:
Expand Down
Loading
Loading