mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-11-11 06:37:21 +01:00
.gemini: add styleguide.md
Add a styleguide for gemini derived from docs/development_guidelines.md.
This commit is contained in:
329
.gemini/styleguide.md
Normal file
329
.gemini/styleguide.md
Normal file
@@ -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:
|
||||||
|
<code block>
|
||||||
|
case b:
|
||||||
|
<code block>
|
||||||
|
case c:
|
||||||
|
<code block>
|
||||||
|
case d:
|
||||||
|
<code block>
|
||||||
|
default:
|
||||||
|
<code block>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
**RIGHT**
|
||||||
|
```go
|
||||||
|
switch {
|
||||||
|
// Brief comment detailing instances of this case (repeat below).
|
||||||
|
case a:
|
||||||
|
<code block>
|
||||||
|
|
||||||
|
case b:
|
||||||
|
<code block>
|
||||||
|
|
||||||
|
case c:
|
||||||
|
<code block>
|
||||||
|
|
||||||
|
case d:
|
||||||
|
<code block>
|
||||||
|
|
||||||
|
default:
|
||||||
|
<code block>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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.
|
||||||
Reference in New Issue
Block a user