github.com/decred/dcrlnd@v0.7.6/chanbackup/single.go (about) 1 package chanbackup 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "net" 8 9 "github.com/decred/dcrd/chaincfg/chainhash" 10 "github.com/decred/dcrd/dcrec/secp256k1/v4" 11 "github.com/decred/dcrd/dcrutil/v4" 12 "github.com/decred/dcrd/wire" 13 "github.com/decred/dcrlnd/channeldb" 14 "github.com/decred/dcrlnd/keychain" 15 "github.com/decred/dcrlnd/lnwire" 16 ) 17 18 // SingleBackupVersion denotes the version of the single static channel backup. 19 // Based on this version, we know how to pack/unpack serialized versions of the 20 // backup. 21 type SingleBackupVersion byte 22 23 const ( 24 // DefaultSingleVersion is the default version of the single channel 25 // backup. The serialized version of this static channel backup is 26 // simply: version || SCB. Where SCB is the known format of the 27 // version. 28 DefaultSingleVersion = 0 29 30 // TweaklessCommitVersion is the second SCB version. This version 31 // implicitly denotes that this channel uses the new tweakless commit 32 // format. 33 TweaklessCommitVersion = 1 34 35 // AnchorsCommitVersion is the third SCB version. This version 36 // implicitly denotes that this channel uses the new anchor commitment 37 // format. 38 AnchorsCommitVersion = 2 39 40 // AnchorsZeroFeeHtlcTxCommitVersion is a version that denotes this 41 // channel is using the zero-fee second-level anchor commitment format. 42 AnchorsZeroFeeHtlcTxCommitVersion = 3 43 44 // ScriptEnforcedLeaseVersion is a version that denotes this channel is 45 // using the zero-fee second-level anchor commitment format along with 46 // an additional CLTV requirement of the channel lease maturity on any 47 // commitment and HTLC outputs that pay directly to the channel 48 // initiator. 49 ScriptEnforcedLeaseVersion = 4 50 ) 51 52 // Single is a static description of an existing channel that can be used for 53 // the purposes of backing up. The fields in this struct allow a node to 54 // recover the settled funds within a channel in the case of partial or 55 // complete data loss. We provide the network address that we last used to 56 // connect to the peer as well, in case the node stops advertising the IP on 57 // the network for whatever reason. 58 // 59 // TODO(roasbeef): suffix version into struct? 60 type Single struct { 61 // Version is the version that should be observed when attempting to 62 // pack the single backup. 63 Version SingleBackupVersion 64 65 // IsInitiator is true if we were the initiator of the channel, and 66 // false otherwise. We'll need to know this information in order to 67 // properly re-derive the state hint information. 68 IsInitiator bool 69 70 // ChainHash is a hash which represents the blockchain that this 71 // channel will be opened within. This value is typically the genesis 72 // hash. In the case that the original chain went through a contentious 73 // hard-fork, then this value will be tweaked using the unique fork 74 // point on each branch. 75 ChainHash chainhash.Hash 76 77 // FundingOutpoint is the outpoint of the final funding transaction. 78 // This value uniquely and globally identities the channel within the 79 // target blockchain as specified by the chain hash parameter. 80 FundingOutpoint wire.OutPoint 81 82 // ShortChannelID encodes the exact location in the chain in which the 83 // channel was initially confirmed. This includes: the block height, 84 // transaction index, and the output within the target transaction. 85 // Channels that were not confirmed at the time of backup creation will 86 // have the funding TX broadcast height set as their block height in 87 // the ShortChannelID. 88 ShortChannelID lnwire.ShortChannelID 89 90 // RemoteNodePub is the identity public key of the remote node this 91 // channel has been established with. 92 RemoteNodePub *secp256k1.PublicKey 93 94 // Addresses is a list of IP address in which either we were able to 95 // reach the node over in the past, OR we received an incoming 96 // authenticated connection for the stored identity public key. 97 Addresses []net.Addr 98 99 // Capacity is the size of the original channel. 100 Capacity dcrutil.Amount 101 102 // LocalChanCfg is our local channel configuration. It contains all the 103 // information we need to re-derive the keys we used within the 104 // channel. Most importantly, it allows to derive the base public 105 // that's used to deriving the key used within the non-delayed 106 // pay-to-self output on the commitment transaction for a node. With 107 // this information, we can re-derive the private key needed to sweep 108 // the funds on-chain. 109 // 110 // NOTE: Of the items in the ChannelConstraints, we only write the CSV 111 // delay. 112 LocalChanCfg channeldb.ChannelConfig 113 114 // RemoteChanCfg is the remote channel confirmation. We store this as 115 // well since we'll need some of their keys to re-derive things like 116 // the state hint obfuscator which will allow us to recognize the state 117 // their broadcast on chain. 118 // 119 // NOTE: Of the items in the ChannelConstraints, we only write the CSV 120 // delay. 121 RemoteChanCfg channeldb.ChannelConfig 122 123 // ShaChainRootDesc describes how to derive the private key that was 124 // used as the shachain root for this channel. 125 ShaChainRootDesc keychain.KeyDescriptor 126 127 // LeaseExpiry represents the absolute expiration as a height of the 128 // chain of a channel lease that is applied to every output that pays 129 // directly to the channel initiator in addition to the usual CSV 130 // requirement. 131 // 132 // NOTE: This field will only be present for the following versions: 133 // 134 // - ScriptEnforcedLeaseVersion 135 LeaseExpiry uint32 136 } 137 138 // NewSingle creates a new static channel backup based on an existing open 139 // channel. We also pass in the set of addresses that we used in the past to 140 // connect to the channel peer. 141 func NewSingle(channel *channeldb.OpenChannel, 142 nodeAddrs []net.Addr) Single { 143 144 var shaChainRootDesc keychain.KeyDescriptor 145 146 // If the channel has a populated RevocationKeyLocator, then we can 147 // just store that instead of the public key. 148 if channel.RevocationKeyLocator.Family == keychain.KeyFamilyRevocationRoot { 149 shaChainRootDesc = keychain.KeyDescriptor{ 150 KeyLocator: channel.RevocationKeyLocator, 151 } 152 } else { 153 // If the RevocationKeyLocator is not populated, then we'll need 154 // to obtain a public point for the shachain root and store that. 155 // This is the legacy scheme. 156 var b bytes.Buffer 157 _ = channel.RevocationProducer.Encode(&b) // Can't return an error. 158 159 // Once we have the root, we'll make a public key from it, such that 160 // the backups plaintext don't carry any private information. When we 161 // go to recover, we'll present this in order to derive the private 162 // key. 163 shaChainPoint := secp256k1.PrivKeyFromBytes(b.Bytes()).PubKey() 164 165 shaChainRootDesc = keychain.KeyDescriptor{ 166 PubKey: shaChainPoint, 167 KeyLocator: keychain.KeyLocator{ 168 Family: keychain.KeyFamilyRevocationRoot, 169 }, 170 } 171 } 172 173 // If a channel is unconfirmed, the block height of the ShortChannelID 174 // is zero. This will lead to problems when trying to restore that 175 // channel as the spend notifier would get a height hint of zero. 176 // To work around that problem, we add the channel broadcast height 177 // to the channel ID so we can use that as height hint on restore. 178 chanID := channel.ShortChanID() 179 if chanID.BlockHeight == 0 { 180 chanID.BlockHeight = channel.FundingBroadcastHeight 181 } 182 183 single := Single{ 184 IsInitiator: channel.IsInitiator, 185 ChainHash: channel.ChainHash, 186 FundingOutpoint: channel.FundingOutpoint, 187 ShortChannelID: chanID, 188 RemoteNodePub: channel.IdentityPub, 189 Addresses: nodeAddrs, 190 Capacity: channel.Capacity, 191 LocalChanCfg: channel.LocalChanCfg, 192 RemoteChanCfg: channel.RemoteChanCfg, 193 ShaChainRootDesc: shaChainRootDesc, 194 } 195 196 switch { 197 case channel.ChanType.HasLeaseExpiration(): 198 single.Version = ScriptEnforcedLeaseVersion 199 single.LeaseExpiry = channel.ThawHeight 200 201 case channel.ChanType.ZeroHtlcTxFee(): 202 single.Version = AnchorsZeroFeeHtlcTxCommitVersion 203 204 case channel.ChanType.HasAnchors(): 205 single.Version = AnchorsCommitVersion 206 207 case channel.ChanType.IsTweakless(): 208 single.Version = TweaklessCommitVersion 209 210 default: 211 single.Version = DefaultSingleVersion 212 } 213 214 return single 215 } 216 217 // Serialize attempts to write out the serialized version of the target 218 // StaticChannelBackup into the passed io.Writer. 219 func (s *Single) Serialize(w io.Writer) error { 220 // Check to ensure that we'll only attempt to serialize a version that 221 // we're aware of. 222 switch s.Version { 223 case DefaultSingleVersion: 224 case TweaklessCommitVersion: 225 case AnchorsCommitVersion: 226 case AnchorsZeroFeeHtlcTxCommitVersion: 227 case ScriptEnforcedLeaseVersion: 228 default: 229 return fmt.Errorf("unable to serialize w/ unknown "+ 230 "version: %v", s.Version) 231 } 232 233 // If the sha chain root has specified a public key (which is 234 // optional), then we'll encode it now. 235 var shaChainPub [33]byte 236 if s.ShaChainRootDesc.PubKey != nil { 237 copy( 238 shaChainPub[:], 239 s.ShaChainRootDesc.PubKey.SerializeCompressed(), 240 ) 241 } 242 243 // First we gather the SCB as is into a temporary buffer so we can 244 // determine the total length. Before we write out the serialized SCB, 245 // we write the length which allows us to skip any Singles that we 246 // don't know of when decoding a multi. 247 var singleBytes bytes.Buffer 248 if err := lnwire.WriteElements( 249 &singleBytes, 250 s.IsInitiator, 251 s.ChainHash[:], 252 s.FundingOutpoint, 253 s.ShortChannelID, 254 s.RemoteNodePub, 255 s.Addresses, 256 s.Capacity, 257 258 s.LocalChanCfg.CsvDelay, 259 260 // We only need to write out the KeyLocator portion of the 261 // local channel config. 262 uint32(s.LocalChanCfg.MultiSigKey.Family), 263 s.LocalChanCfg.MultiSigKey.Index, 264 uint32(s.LocalChanCfg.RevocationBasePoint.Family), 265 s.LocalChanCfg.RevocationBasePoint.Index, 266 uint32(s.LocalChanCfg.PaymentBasePoint.Family), 267 s.LocalChanCfg.PaymentBasePoint.Index, 268 uint32(s.LocalChanCfg.DelayBasePoint.Family), 269 s.LocalChanCfg.DelayBasePoint.Index, 270 uint32(s.LocalChanCfg.HtlcBasePoint.Family), 271 s.LocalChanCfg.HtlcBasePoint.Index, 272 273 s.RemoteChanCfg.CsvDelay, 274 275 // We only need to write out the raw pubkey for the remote 276 // channel config. 277 s.RemoteChanCfg.MultiSigKey.PubKey, 278 s.RemoteChanCfg.RevocationBasePoint.PubKey, 279 s.RemoteChanCfg.PaymentBasePoint.PubKey, 280 s.RemoteChanCfg.DelayBasePoint.PubKey, 281 s.RemoteChanCfg.HtlcBasePoint.PubKey, 282 283 shaChainPub[:], 284 uint32(s.ShaChainRootDesc.KeyLocator.Family), 285 s.ShaChainRootDesc.KeyLocator.Index, 286 ); err != nil { 287 return err 288 } 289 if s.Version == ScriptEnforcedLeaseVersion { 290 err := lnwire.WriteElements(&singleBytes, s.LeaseExpiry) 291 if err != nil { 292 return err 293 } 294 } 295 296 // TODO(yy): remove the type assertion when we finished refactoring db 297 // into using write buffer. 298 buf, ok := w.(*bytes.Buffer) 299 if !ok { 300 return fmt.Errorf("expect io.Writer to be *bytes.Buffer") 301 } 302 303 return lnwire.WriteElements( 304 buf, 305 byte(s.Version), 306 uint16(len(singleBytes.Bytes())), 307 singleBytes.Bytes(), 308 ) 309 } 310 311 // PackToWriter is similar to the Serialize method, but takes the operation a 312 // step further by encryption the raw bytes of the static channel back up. For 313 // encryption we use the chacah20poly1305 AEAD cipher with a 24 byte nonce and 314 // 32-byte key size. We use a 24-byte nonce, as we can't ensure that we have a 315 // global counter to use as a sequence number for nonces, and want to ensure 316 // that we're able to decrypt these blobs without any additional context. We 317 // derive the key that we use for encryption via a SHA2 operation of the with 318 // the golden keychain.KeyFamilyStaticBackup base encryption key. We then take 319 // the serialized resulting shared secret point, and hash it using sha256 to 320 // obtain the key that we'll use for encryption. When using the AEAD, we pass 321 // the nonce as associated data such that we'll be able to package the two 322 // together for storage. Before writing out the encrypted payload, we prepend 323 // the nonce to the final blob. 324 func (s *Single) PackToWriter(w io.Writer, keyRing keychain.KeyRing) error { 325 // First, we'll serialize the SCB (StaticChannelBackup) into a 326 // temporary buffer so we can store it in a temporary place before we 327 // go to encrypt the entire thing. 328 var rawBytes bytes.Buffer 329 if err := s.Serialize(&rawBytes); err != nil { 330 return err 331 } 332 333 // Finally, we'll encrypt the raw serialized SCB (using the nonce as 334 // associated data), and write out the ciphertext prepend with the 335 // nonce that we used to the passed io.Reader. 336 return encryptPayloadToWriter(rawBytes, w, keyRing) 337 } 338 339 // readLocalKeyDesc reads a KeyDescriptor encoded within an unpacked Single. 340 // For local KeyDescs, we only write out the KeyLocator information as we can 341 // re-derive the pubkey from it. 342 func readLocalKeyDesc(r io.Reader) (keychain.KeyDescriptor, error) { 343 var keyDesc keychain.KeyDescriptor 344 345 var keyFam uint32 346 if err := lnwire.ReadElements(r, &keyFam); err != nil { 347 return keyDesc, err 348 } 349 keyDesc.Family = keychain.KeyFamily(keyFam) 350 351 if err := lnwire.ReadElements(r, &keyDesc.Index); err != nil { 352 return keyDesc, err 353 } 354 355 return keyDesc, nil 356 } 357 358 // readRemoteKeyDesc reads a remote KeyDescriptor encoded within an unpacked 359 // Single. For remote KeyDescs, we write out only the PubKey since we don't 360 // actually have the KeyLocator data. 361 func readRemoteKeyDesc(r io.Reader) (keychain.KeyDescriptor, error) { 362 var ( 363 keyDesc keychain.KeyDescriptor 364 pub [33]byte 365 ) 366 367 _, err := io.ReadFull(r, pub[:]) 368 if err != nil { 369 return keychain.KeyDescriptor{}, err 370 } 371 372 keyDesc.PubKey, err = secp256k1.ParsePubKey(pub[:]) 373 if err != nil { 374 return keychain.KeyDescriptor{}, err 375 } 376 377 return keyDesc, nil 378 } 379 380 // Deserialize attempts to read the raw plaintext serialized SCB from the 381 // passed io.Reader. If the method is successful, then the target 382 // StaticChannelBackup will be fully populated. 383 func (s *Single) Deserialize(r io.Reader) error { 384 // First, we'll need to read the version of this single-back up so we 385 // can know how to unpack each of the SCB. 386 var version byte 387 err := lnwire.ReadElements(r, &version) 388 if err != nil { 389 return err 390 } 391 392 s.Version = SingleBackupVersion(version) 393 394 switch s.Version { 395 case DefaultSingleVersion: 396 case TweaklessCommitVersion: 397 case AnchorsCommitVersion: 398 case AnchorsZeroFeeHtlcTxCommitVersion: 399 case ScriptEnforcedLeaseVersion: 400 default: 401 return fmt.Errorf("unable to de-serialize w/ unknown "+ 402 "version: %v", s.Version) 403 } 404 405 var length uint16 406 if err := lnwire.ReadElements(r, &length); err != nil { 407 return err 408 } 409 410 err = lnwire.ReadElements( 411 r, &s.IsInitiator, s.ChainHash[:], &s.FundingOutpoint, 412 &s.ShortChannelID, &s.RemoteNodePub, &s.Addresses, &s.Capacity, 413 ) 414 if err != nil { 415 return err 416 } 417 418 err = lnwire.ReadElements(r, &s.LocalChanCfg.CsvDelay) 419 if err != nil { 420 return err 421 } 422 s.LocalChanCfg.MultiSigKey, err = readLocalKeyDesc(r) 423 if err != nil { 424 return err 425 } 426 s.LocalChanCfg.RevocationBasePoint, err = readLocalKeyDesc(r) 427 if err != nil { 428 return err 429 } 430 s.LocalChanCfg.PaymentBasePoint, err = readLocalKeyDesc(r) 431 if err != nil { 432 return err 433 } 434 s.LocalChanCfg.DelayBasePoint, err = readLocalKeyDesc(r) 435 if err != nil { 436 return err 437 } 438 s.LocalChanCfg.HtlcBasePoint, err = readLocalKeyDesc(r) 439 if err != nil { 440 return err 441 } 442 443 err = lnwire.ReadElements(r, &s.RemoteChanCfg.CsvDelay) 444 if err != nil { 445 return err 446 } 447 s.RemoteChanCfg.MultiSigKey, err = readRemoteKeyDesc(r) 448 if err != nil { 449 return err 450 } 451 s.RemoteChanCfg.RevocationBasePoint, err = readRemoteKeyDesc(r) 452 if err != nil { 453 return err 454 } 455 s.RemoteChanCfg.PaymentBasePoint, err = readRemoteKeyDesc(r) 456 if err != nil { 457 return err 458 } 459 s.RemoteChanCfg.DelayBasePoint, err = readRemoteKeyDesc(r) 460 if err != nil { 461 return err 462 } 463 s.RemoteChanCfg.HtlcBasePoint, err = readRemoteKeyDesc(r) 464 if err != nil { 465 return err 466 } 467 468 // Finally, we'll parse out the ShaChainRootDesc. 469 var ( 470 shaChainPub [33]byte 471 zeroPub [33]byte 472 ) 473 if err := lnwire.ReadElements(r, shaChainPub[:]); err != nil { 474 return err 475 } 476 477 // Since this field is optional, we'll check to see if the pubkey has 478 // been specified or not. 479 if !bytes.Equal(shaChainPub[:], zeroPub[:]) { 480 s.ShaChainRootDesc.PubKey, err = secp256k1.ParsePubKey( 481 shaChainPub[:], 482 ) 483 if err != nil { 484 return err 485 } 486 } 487 488 var shaKeyFam uint32 489 if err := lnwire.ReadElements(r, &shaKeyFam); err != nil { 490 return err 491 } 492 s.ShaChainRootDesc.KeyLocator.Family = keychain.KeyFamily(shaKeyFam) 493 err = lnwire.ReadElements(r, &s.ShaChainRootDesc.KeyLocator.Index) 494 if err != nil { 495 return err 496 } 497 498 if s.Version == ScriptEnforcedLeaseVersion { 499 if err := lnwire.ReadElement(r, &s.LeaseExpiry); err != nil { 500 return err 501 } 502 } 503 504 return nil 505 } 506 507 // UnpackFromReader is similar to Deserialize method, but it expects the passed 508 // io.Reader to contain an encrypt SCB. Refer to the SerializeAndEncrypt method 509 // for details w.r.t the encryption scheme used. If we're unable to decrypt the 510 // payload for whatever reason (wrong key, wrong nonce, etc), then this method 511 // will return an error. 512 func (s *Single) UnpackFromReader(r io.Reader, keyRing keychain.KeyRing) error { 513 plaintext, err := decryptPayloadFromReader(r, keyRing) 514 if err != nil { 515 return err 516 } 517 518 // Finally, we'll pack the bytes into a reader to we can deserialize 519 // the plaintext bytes of the SCB. 520 backupReader := bytes.NewReader(plaintext) 521 return s.Deserialize(backupReader) 522 } 523 524 // PackStaticChanBackups accepts a set of existing open channels, and a 525 // keychain.KeyRing, and returns a map of outpoints to the serialized+encrypted 526 // static channel backups. The passed keyRing should be backed by the users 527 // root HD seed in order to ensure full determinism. 528 func PackStaticChanBackups(backups []Single, 529 keyRing keychain.KeyRing) (map[wire.OutPoint][]byte, error) { 530 531 packedBackups := make(map[wire.OutPoint][]byte) 532 for _, chanBackup := range backups { 533 chanPoint := chanBackup.FundingOutpoint 534 535 var b bytes.Buffer 536 err := chanBackup.PackToWriter(&b, keyRing) 537 if err != nil { 538 return nil, fmt.Errorf("unable to pack chan backup "+ 539 "for %v: %v", chanPoint, err) 540 } 541 542 packedBackups[chanPoint] = b.Bytes() 543 } 544 545 return packedBackups, nil 546 } 547 548 // PackedSingles represents a series of fully packed SCBs. This may be the 549 // combination of a series of individual SCBs in order to batch their 550 // unpacking. 551 type PackedSingles [][]byte 552 553 // Unpack attempts to decrypt the passed set of encrypted SCBs and deserialize 554 // each one into a new SCB struct. The passed keyRing should be backed by the 555 // same HD seed as was used to encrypt the set of backups in the first place. 556 // If we're unable to decrypt any of the back ups, then we'll return an error. 557 func (p PackedSingles) Unpack(keyRing keychain.KeyRing) ([]Single, error) { 558 559 backups := make([]Single, len(p)) 560 for i, encryptedBackup := range p { 561 var backup Single 562 563 backupReader := bytes.NewReader(encryptedBackup) 564 err := backup.UnpackFromReader(backupReader, keyRing) 565 if err != nil { 566 return nil, err 567 } 568 569 backups[i] = backup 570 } 571 572 return backups, nil 573 } 574 575 // TODO(roasbeef): make codec package?