From b62793ada89cc4e48bf5dac67b94bebed48456d1 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 9 Jul 2025 10:56:31 +0200 Subject: [PATCH] .gemini: add styleguide.md Add a styleguide for gemini derived from docs/development_guidelines.md. --- .gemini/styleguide.md | 329 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 .gemini/styleguide.md diff --git a/.gemini/styleguide.md b/.gemini/styleguide.md new file mode 100644 index 000000000..80466dbaf --- /dev/null +++ b/.gemini/styleguide.md @@ -0,0 +1,329 @@ +# LND Style Guide + +## Code Documentation and Commenting + +- Always use the Golang code style described below in this document. +- Readable code is the most important requirement for any commit created. +- Comments must not explain the code 1:1 but instead explain the _why_ behind a + certain block of code, in case it requires contextual knowledge. +- Unit tests must always use the `require` library. Either table driven unit + tests or tests using the `rapid` library are preferred. +- The line length MUST NOT exceed 80 characters, this is very important. + You must count the Golang indentation (tabulator character) as 8 spaces when + determining the line length. Use creative approaches or the wrapping rules + specified below to make sure the line length isn't exceeded. +- Every function must be commented with its purpose and assumptions. +- Function comments must begin with the function name. +- Function comments should be complete sentences. +- Exported functions require detailed comments for the caller. + +**WRONG** +```go +// generates a revocation key +func DeriveRevocationPubkey(commitPubKey *btcec.PublicKey, + revokePreimage []byte) *btcec.PublicKey { +``` +**RIGHT** +```go +// DeriveRevocationPubkey derives the revocation public key given the +// counterparty's commitment key, and revocation preimage derived via a +// pseudo-random-function. In the event that we (for some reason) broadcast a +// revoked commitment transaction, then if the other party knows the revocation +// preimage, then they'll be able to derive the corresponding private key to +// this private key by exploiting the homomorphism in the elliptic curve group. +// +// The derivation is performed as follows: +// +// revokeKey := commitKey + revokePoint +// := G*k + G*h +// := G * (k+h) +// +// Therefore, once we divulge the revocation preimage, the remote peer is able +// to compute the proper private key for the revokeKey by computing: +// revokePriv := commitPriv + revokePreimge mod N +// +// Where N is the order of the sub-group. +func DeriveRevocationPubkey(commitPubKey *btcec.PublicKey, + revokePreimage []byte) *btcec.PublicKey { +``` +- In-body comments should explain the *intention* of the code. + +**WRONG** +```go +// return err if amt is less than 546 +if amt < 546 { + return err +} +``` +**RIGHT** +```go +// Treat transactions with amounts less than the amount which is considered dust +// as non-standard. +if amt < 546 { + return err +} +``` + +## Code Spacing and formatting + +- Segment code into logical stanzas separated by newlines. + +**WRONG** +```go + witness := make([][]byte, 4) + witness[0] = nil + if bytes.Compare(pubA, pubB) == -1 { + witness[1] = sigB + witness[2] = sigA + } else { + witness[1] = sigA + witness[2] = sigB + } + witness[3] = witnessScript + return witness +``` +**RIGHT** +```go + witness := make([][]byte, 4) + + // When spending a p2wsh multi-sig script, rather than an OP_0, we add + // a nil stack element to eat the extra pop. + witness[0] = nil + + // When initially generating the witnessScript, we sorted the serialized + // public keys in descending order. So we do a quick comparison in order + // to ensure the signatures appear on the Script Virtual Machine stack in + // the correct order. + if bytes.Compare(pubA, pubB) == -1 { + witness[1] = sigB + witness[2] = sigA + } else { + witness[1] = sigA + witness[2] = sigB + } + + // Finally, add the preimage as the last witness element. + witness[3] = witnessScript + + return witness +``` + +- Use spacing between `case` and `select` stanzas. + +**WRONG** +```go + switch { + case a: + + case b: + + case c: + + case d: + + default: + + } +``` +**RIGHT** +```go + switch { + // Brief comment detailing instances of this case (repeat below). + case a: + + + case b: + + + case c: + + + case d: + + + default: + + } +``` + +## Additional Style Constraints + +### 80 character line length + +- Wrap columns at 80 characters. +- Tabs are 8 spaces. + +**WRONG** +```go +myKey := "0214cd678a565041d00e6cf8d62ef8add33b4af4786fb2beb87b366a2e151fcee7" +``` + +**RIGHT** +```go +myKey := "0214cd678a565041d00e6cf8d62ef8add33b4af4786fb2beb87b366a2e1" + + "51fcee7" +``` + +### Wrapping long function calls + +- If a function call exceeds the column limit, place the closing parenthesis + on its own line and start all arguments on a new line after the opening + parenthesis. + +**WRONG** +```go +value, err := bar(a, + a, b, c) +``` + +**RIGHT** +```go +value, err := bar( + a, a, b, c, +) +``` + +- Compact form is acceptable if visual symmetry of parentheses is preserved. + +**ACCEPTABLE** +```go + response, err := node.AddInvoice( + ctx, &lnrpc.Invoice{ + Memo: "invoice", + ValueMsat: int64(oneUnitMilliSat - 1), + }, + ) +``` + +**PREFERRED** +```go + response, err := node.AddInvoice(ctx, &lnrpc.Invoice{ + Memo: "invoice", + ValueMsat: int64(oneUnitMilliSat - 1), + }) +``` + +### Exception for log and error message formatting + +- Minimize lines for log and error messages, while adhering to the + 80-character limit. + +**WRONG** +```go +return fmt.Errorf( + "this is a long error message with a couple (%d) place holders", + len(things), +) + +log.Debugf( + "Something happened here that we need to log: %v", + longVariableNameHere, +) +``` + +**RIGHT** +```go +return fmt.Errorf("this is a long error message with a couple (%d) place "+ + "holders", len(things)) + +log.Debugf("Something happened here that we need to log: %v", + longVariableNameHere) +``` + +### Exceptions and additional styling for structured logging + +- **Static messages:** Use key-value pairs instead of formatted strings for the + `msg` parameter. +- **Key-value attributes:** Use `slog.Attr` helper functions. +- **Line wrapping:** Structured log lines are an exception to the 80-character + rule. Use one line per key-value pair for multiple attributes. + +**WRONG** +```go +log.DebugS(ctx, fmt.Sprintf("User %d just spent %.8f to open a channel", userID, 0.0154)) +``` + +**RIGHT** +```go +log.InfoS(ctx, "Channel open performed", + slog.Int("user_id", userID), + btclog.Fmt("amount", "%.8f", 0.00154)) +``` + +### Wrapping long function definitions + +- If function arguments exceed the 80-character limit, maintain indentation + on following lines. +- Do not end a line with an open parenthesis if the function definition is not + finished. + +**WRONG** +```go +func foo(a, b, c, +) (d, error) { + +func bar(a, b, c) ( + d, error, +) { + +func baz(a, b, c) ( + d, error) { +``` +**RIGHT** +```go +func foo(a, b, + c) (d, error) { + +func baz(a, b, c) (d, + error) { + +func longFunctionName( + a, b, c) (d, error) { +``` + +- If a function declaration spans multiple lines, the body should start with an + empty line. + +**WRONG** +```go +func foo(a, b, c, + d, e) error { + var a int +} +``` +**RIGHT** +```go +func foo(a, b, c, + d, e) error { + + var a int +} +``` + +## Use of Log Levels + +- Available levels: `trace`, `debug`, `info`, `warn`, `error`, `critical`. +- Only use `error` for internal errors not triggered by external sources. + +## Testing + +- To run all tests for a specific package: + `make unit pkg=$pkg` +- To run a specific test case within a package: + `make unit pkg=$pkg case=$case` + +## Git Commit Messages + +- **Subject Line:** + - Format: `subsystem: short description of changes` + - `subsystem` should be the package primarily affected (e.g., `lnwallet`, `rpcserver`). + - For multiple packages, use `+` or `,` as a delimiter (e.g., `lnwallet+htlcswitch`). + - For widespread changes, use `multi:`. + - Keep it under 50 characters. + - Use the present tense (e.g., "Fix bug", not "Fixed bug"). + +- **Message Body:** + - Separate from the subject with a blank line. + - Explain the "what" and "why" of the change. + - Wrap text to 72 characters. + - Use bullet points for lists.