# Usage Rules & Quick Reference

Quick reference for `lib_secp256k1` library users. For detailed examples, see [Usage Guide](usage.md) and [MuSig Guide](musig.md).

## Data Sizes

| Type                  | Size       | Description                       |
| --------------------- | ---------- | --------------------------------- |
| `seckey`              | 32 bytes   | Secret key (private key)          |
| `tweak`               | 32 bytes   | Big-endian scalar for key tweaks  |
| `hash`                | 32 bytes   | Message hash (SHA256)             |
| `compressed_pubkey`   | 33 bytes   | Standard Bitcoin pubkey format    |
| `uncompressed_pubkey` | 65 bytes   | Full pubkey with both coordinates |
| `xonly_pubkey`        | 32 bytes   | Schnorr/Taproot/Nostr format      |
| `ecdsa_sig`           | 64 bytes   | Compact ECDSA signature           |
| `ecdsa_der_sig`       | 8-72 bytes | Strict DER ECDSA signature        |
| `schnorr_sig`         | 64 bytes   | BIP-340 Schnorr signature         |

## Quick Reference

### Keypairs

```elixir
# Generate random keypair
{seckey, pubkey} = Secp256k1.keypair(:compressed)   # 33-byte pubkey
{seckey, pubkey} = Secp256k1.keypair(:xonly)        # 32-byte pubkey (Schnorr)
{seckey, pubkey} = Secp256k1.keypair(:uncompressed) # 65-byte pubkey

# Derive pubkey from existing seckey
pubkey = Secp256k1.pubkey(seckey, :compressed)

# Convert a received compressed pubkey without its seckey
xonly_pubkey = Secp256k1.convert_pubkey(pubkey, :xonly)

# Validate externally received keys, including their cryptographic encoding
true = Secp256k1.valid_seckey?(seckey)
true = Secp256k1.valid_pubkey?(pubkey)
```

### Key Tweaks (BIP-32 and Taproot)

```elixir
# Raw arithmetic example only. Derive this scalar according to BIP-32 or BIP-341.
tweak = <<1::256>>

tweaked_seckey = Secp256k1.ec_seckey_tweak_add(seckey, tweak)
tweaked_pubkey = Secp256k1.ec_pubkey_tweak_add(pubkey, tweak)

internal_pubkey = Secp256k1.pubkey(seckey, :xonly)
{:ok, output_pubkey, parity} = Secp256k1.xonly_pubkey_tweak_add(internal_pubkey, tweak)
true = Secp256k1.xonly_pubkey_tweak_add_check(output_pubkey, parity, internal_pubkey, tweak)
output_seckey = Secp256k1.xonly_seckey_tweak_add(seckey, tweak)
```

### ECDSA (Bitcoin legacy)

```elixir
msg_hash = :crypto.hash(:sha256, "message")  # MUST be 32 bytes
signature = Secp256k1.ecdsa_sign(msg_hash, seckey)
der_signature = Secp256k1.ecdsa_signature_serialize_der(signature)
signature = Secp256k1.ecdsa_signature_parse_der(der_signature)

# Only when the protocol deliberately accepts malleable high-S forms:
signature = Secp256k1.ecdsa_signature_normalize(signature)
true = Secp256k1.ecdsa_valid?(signature, msg_hash, pubkey)  # compressed or uncompressed pubkey
```

### Schnorr (BIP-340, Taproot, Nostr)

```elixir
msg_hash = :crypto.hash(:sha256, "message")
signature = Secp256k1.schnorr_sign(msg_hash, seckey)
true = Secp256k1.schnorr_valid?(signature, msg_hash, xonly_pubkey)  # x-only pubkey
```

## Rules

### DO

- **Hash messages before signing**: Always pass a 32-byte hash to signing functions, not raw messages.
- **Use secp256k1 pubkeys for ECDSA**: `ecdsa_valid?/3` accepts 33-byte compressed or 65-byte uncompressed pubkeys.
- **Reject high-S by default**: Normalize only when the protocol deliberately accepts malleable signature forms, then use the normalized bytes thereafter.
- **Separate Bitcoin sighash bytes**: DER conversion handles only the signature, not a trailing transaction sighash byte.
- **Use x-only pubkeys for Schnorr**: `schnorr_valid?/3` expects 32-byte x-only pubkeys.
- **Generate fresh keypairs securely**: `Secp256k1.keypair/1` uses `:crypto.strong_rand_bytes/1`.
- **Validate inputs early**: Use `valid_seckey?/1` and `valid_pubkey?/1` for externally received keys.
- **Keep x-only output parity**: Taproot tweak verification requires both the output key and parity.

### DON'T

- **Don't reuse nonces in MuSig2**: Call `nonce_gen/5` fresh for every signature attempt. Nonce reuse leaks the secret key.
- **Don't use custom Schnorr AUX values**: `sign32/3` exists but is NOT RECOMMENDED. Use the 2-arg version.
- **Don't mix pubkey formats**: ECDSA uses compressed (33 bytes) or uncompressed (65 bytes); Schnorr uses x-only (32 bytes).
- **Don't sign unhashed data**: The library expects pre-hashed 32-byte messages for most operations.
- **Don't serialize MuSig secnonces**: They're Erlang resources, not binaries. Attempting to copy them will fail.
- **Don't treat key tweaking as hashing**: Derive the scalar according to BIP-32 or BIP-341 before calling the tweak API.

## Error Handling

```elixir
# Stable operations raise FunctionClauseError for invalid-sized binary inputs
try do
  Secp256k1.ecdsa_sign(<<1, 2, 3>>, seckey)  # msg_hash too short
rescue
  FunctionClauseError -> # handle invalid size
end

# Key validation predicates always return a boolean
false = Secp256k1.valid_seckey?(<<1, 2, 3>>)
false = Secp256k1.valid_pubkey?(:not_a_key)

# MuSig functions return {:error, reason} tuples
case Secp256k1.MuSig.pubkey_agg(pubkeys) do
  {:ok, agg_pubkey, cache} -> # success
  {:error, reason} -> # handle error
end
```

## Common Mistakes

| Mistake                 | Problem                                | Fix                                                        |
| ----------------------- | -------------------------------------- | ---------------------------------------------------------- |
| Signing raw message     | Library expects 32-byte hash           | Use `:crypto.hash(:sha256, msg)` first                     |
| Wrong pubkey type       | ECDSA/Schnorr use different formats    | ECDSA: `:compressed` or `:uncompressed`, Schnorr: `:xonly` |
| Reusing MuSig nonces    | Leaks secret key                       | Always call `nonce_gen/5` fresh                            |
| Invalid binary size     | Operations raise `FunctionClauseError` | Use documented sizes; key predicates return `false`        |
| Invalid key encoding    | Correct size does not imply validity   | Use `valid_seckey?/1` or `valid_pubkey?/1` before use      |
| Passing DER to verify   | Verification expects compact signature | Parse DER; normalize only if the protocol permits high-S   |
| Forgetting to aggregate | MuSig requires full protocol           | Follow all 6 steps in MuSig guide                          |

## MuSig2 Protocol (Summary)

```elixir
# 1. Aggregate pubkeys
{:ok, agg_pubkey, cache} = MuSig.pubkey_agg(pubkeys)

# 2. Generate nonces (each signer)
{:ok, secnonce, pubnonce} = MuSig.nonce_gen(seckey, pubkey, msg, cache, nil)

# 3. Aggregate nonces
aggnonce = MuSig.nonce_agg(pubnonces)

# 4. Create session
session = MuSig.nonce_process(aggnonce, msg, cache)

# 5. Partial sign (each signer)
partial_sig = MuSig.partial_sign(secnonce, seckey, cache, session)

# 6. Aggregate signatures
final_sig = MuSig.partial_sig_agg(session, partial_sigs)

# Verify as standard Schnorr
Secp256k1.schnorr_valid?(final_sig, msg, agg_pubkey)
```

## Security Checklist

- [ ] Secret keys generated from secure random source
- [ ] Externally received keys validated before use
- [ ] Secret keys never logged or exposed
- [ ] Messages hashed before signing
- [ ] MuSig nonces never reused
- [ ] MuSig public nonces exchanged before signing begins
- [ ] Signatures verified after receiving from external sources

## Platform Notes

- **Linux**: Primary platform, fully supported
- **macOS**: Supported with Xcode Command Line Tools (`xcode-select --install`)
- **Windows**: Not tested

## Version Compatibility

- Elixir: `~> 1.15`
- Underlying C library: bitcoin-core/secp256k1 v0.7.1
