IMPL5.md (6656B)
1 # IMPL5: Error Handling 2 3 **Module**: `Lightning.Protocol.BOLT4.Error` 4 5 **Dependencies**: IMPL1 (Prim), IMPL2 (Types, Codec) 6 7 **Can run in parallel with**: IMPL3, IMPL4 (after IMPL1 and IMPL2 complete) 8 9 ## Overview 10 11 Implement failure message construction (by failing node) and 12 unwrapping/attribution (by origin node). 13 14 ## Types 15 16 ```haskell 17 -- | Wrapped error packet ready for return to origin. 18 newtype ErrorPacket = ErrorPacket BS.ByteString 19 deriving (Eq, Show) 20 21 -- | Result of error attribution. 22 data AttributionResult 23 = Attributed !Int !FailureMessage -- ^ (hop index, failure) 24 | UnknownOrigin !BS.ByteString -- ^ Could not attribute 25 deriving (Eq, Show) 26 27 -- | Minimum error packet size (256 bytes per spec). 28 minErrorPacketSize :: Int 29 minErrorPacketSize = 256 30 ``` 31 32 ## Error Construction (Failing Node) 33 34 ```haskell 35 -- | Construct an error packet at a failing node. 36 -- 37 -- Takes the shared secret (from processing), failure message, 38 -- and wraps it for return to origin. 39 constructError 40 :: SharedSecret -- ^ from packet processing 41 -> FailureMessage -- ^ failure details 42 -> ErrorPacket 43 44 -- | Wrap an existing error packet for forwarding back. 45 -- 46 -- Each intermediate node wraps the error with its own layer. 47 wrapError 48 :: SharedSecret -- ^ this node's shared secret 49 -> ErrorPacket -- ^ error from downstream 50 -> ErrorPacket 51 ``` 52 53 ## Error Unwrapping (Origin Node) 54 55 ```haskell 56 -- | Attempt to attribute an error to a specific hop. 57 -- 58 -- Takes the shared secrets from original packet construction 59 -- (in order from first hop to final) and the error packet. 60 -- 61 -- Tries each hop's keys until HMAC verifies, revealing origin. 62 unwrapError 63 :: [SharedSecret] -- ^ secrets from construction, in route order 64 -> ErrorPacket -- ^ received error 65 -> AttributionResult 66 ``` 67 68 ## Internal Functions 69 70 ### Error Packet Construction 71 72 ```haskell 73 -- | Build the inner error message structure. 74 -- 75 -- Format: HMAC (32) || len (2) || message || pad_len (2) || padding 76 -- Total must be >= 256 bytes. 77 buildErrorMessage 78 :: DerivedKey -- ^ um key 79 -> FailureMessage -- ^ failure to encode 80 -> BS.ByteString -- ^ complete message with HMAC 81 ``` 82 83 Algorithm: 84 1. Encode failure message to bytes 85 2. Compute padding needed: max(0, 256 - 32 - 2 - msg_len - 2) 86 3. Build: len (u16 BE) || message || pad_len (u16 BE) || padding 87 4. Compute HMAC = HMAC-SHA256(um_key, len || message || pad_len || padding) 88 5. Return: HMAC || len || message || pad_len || padding 89 90 ### Error Obfuscation 91 92 ```haskell 93 -- | Obfuscate error packet with ammag stream. 94 -- 95 -- XORs the entire packet (after HMAC) with pseudo-random stream. 96 obfuscateError 97 :: DerivedKey -- ^ ammag key 98 -> BS.ByteString -- ^ error packet 99 -> BS.ByteString -- ^ obfuscated packet 100 ``` 101 102 Note: The HMAC is computed over plaintext, then the entire packet 103 including HMAC is XORed with ammag stream. 104 105 ### Error Deobfuscation 106 107 ```haskell 108 -- | Remove one layer of obfuscation from error packet. 109 deobfuscateError 110 :: DerivedKey -- ^ ammag key 111 -> BS.ByteString -- ^ obfuscated packet 112 -> BS.ByteString -- ^ deobfuscated packet 113 114 -- | Verify error HMAC after deobfuscation. 115 verifyErrorHmac 116 :: DerivedKey -- ^ um key 117 -> BS.ByteString -- ^ deobfuscated packet (HMAC || rest) 118 -> Bool 119 ``` 120 121 ### Error Parsing 122 123 ```haskell 124 -- | Parse error message from deobfuscated packet. 125 parseErrorMessage 126 :: BS.ByteString -- ^ packet after HMAC verification 127 -> Maybe FailureMessage 128 ``` 129 130 ## Construction Algorithm 131 132 At failing node: 133 134 ``` 135 constructError(shared_secret, failure): 136 1. um = deriveUm(shared_secret) 137 2. ammag = deriveAmmag(shared_secret) 138 3. inner = buildErrorMessage(um, failure) 139 4. obfuscated = obfuscateError(ammag, inner) 140 5. return ErrorPacket(obfuscated) 141 ``` 142 143 At forwarding node (wrapping existing error): 144 145 ``` 146 wrapError(shared_secret, error_packet): 147 1. ammag = deriveAmmag(shared_secret) 148 2. wrapped = obfuscateError(ammag, error_packet) 149 3. return ErrorPacket(wrapped) 150 ``` 151 152 ## Unwrapping Algorithm 153 154 At origin node: 155 156 ``` 157 unwrapError(shared_secrets, error_packet): 158 packet = error_packet.bytes 159 160 for i = 0 to len(shared_secrets) - 1: 161 ammag = deriveAmmag(shared_secrets[i]) 162 um = deriveUm(shared_secrets[i]) 163 164 packet = deobfuscateError(ammag, packet) 165 166 if verifyErrorHmac(um, packet): 167 failure = parseErrorMessage(packet[32:]) 168 return Attributed(i, failure) 169 170 return UnknownOrigin(packet) 171 ``` 172 173 The first hop whose HMAC verifies is the origin of the error. 174 175 ## Failure Codes 176 177 Common failure codes to handle (define in Types, use here): 178 179 ```haskell 180 -- Flags 181 badonion, perm, node, update :: Word16 182 183 -- Codes (incomplete list) 184 invalid_realm = perm .|. 1 185 temporary_node_failure = node .|. 2 186 permanent_node_failure = perm .|. node .|. 2 187 required_node_feature_missing = perm .|. node .|. 3 188 invalid_onion_version = badonion .|. perm .|. 4 189 invalid_onion_hmac = badonion .|. perm .|. 5 190 invalid_onion_key = badonion .|. perm .|. 6 191 temporary_channel_failure = update .|. 7 192 permanent_channel_failure = perm .|. 8 193 amount_below_minimum = update .|. 11 194 fee_insufficient = update .|. 12 195 incorrect_cltv_expiry = update .|. 13 196 expiry_too_soon = update .|. 14 197 incorrect_or_unknown_payment_details = perm .|. 15 198 final_incorrect_cltv_expiry = 18 199 final_incorrect_htlc_amount = 19 200 channel_disabled = update .|. 20 201 expiry_too_far = 21 202 invalid_onion_payload = perm .|. 22 203 mpp_timeout = 23 204 ``` 205 206 ## Implementation Notes 207 208 1. Error packets are always at least 256 bytes to prevent length-based 209 traffic analysis. 210 211 2. Each intermediate node adds a layer of encryption (XOR with ammag 212 stream). Origin peels layers in route order. 213 214 3. HMAC verification at origin: only the actual failing node's HMAC 215 will verify after exactly the right number of layers are removed. 216 217 4. The BADONION flag indicates the error relates to the onion itself 218 (HMAC failure, bad key). These get special treatment. 219 220 5. UPDATE flag means the error includes a channel_update message that 221 the origin should process. 222 223 ## Test Vectors 224 225 From spec, with failure at node 4: 226 227 ``` 228 failure code: incorrect_or_unknown_payment_details (0x400f) 229 htlc_msat: 100 230 height: 800000 231 ``` 232 233 Verify: 234 - Error packet construction produces correct bytes 235 - Wrapping at each intermediate node matches spec 236 - Unwrapping at origin correctly attributes to node 4 237 - Parsed failure message matches original