github.com/decred/dcrlnd@v0.7.6/lntest/itest/lnd_macaroons_test.go (about) 1 package itest 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/hex" 7 "os" 8 "sort" 9 "strconv" 10 "testing" 11 12 "github.com/decred/dcrd/txscript/v4/stdaddr" 13 "github.com/decred/dcrlnd/lnrpc" 14 "github.com/decred/dcrlnd/lntest" 15 "github.com/decred/dcrlnd/macaroons" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 "google.golang.org/protobuf/proto" 19 "gopkg.in/macaroon.v2" 20 ) 21 22 // testMacaroonAuthentication makes sure that if macaroon authentication is 23 // enabled on the gRPC interface, no requests with missing or invalid 24 // macaroons are allowed. Further, the specific access rights (read/write, 25 // entity based) and first-party caveats are tested as well. 26 func testMacaroonAuthentication(net *lntest.NetworkHarness, ht *harnessTest) { 27 var ( 28 infoReq = &lnrpc.GetInfoRequest{} 29 newAddrReq = &lnrpc.NewAddressRequest{ 30 Type: AddrTypePubkeyHash, 31 } 32 testNode = net.Alice 33 ) 34 35 testCases := []struct { 36 name string 37 run func(ctxt context.Context, t *testing.T) 38 }{{ 39 // First test: Make sure we get an error if we use no macaroons 40 // but try to connect to a node that has macaroon authentication 41 // enabled. 42 name: "no macaroon", 43 run: func(ctxt context.Context, t *testing.T) { 44 conn, err := testNode.ConnectRPC(false) 45 require.NoError(t, err) 46 defer func() { _ = conn.Close() }() 47 client := lnrpc.NewLightningClient(conn) 48 _, err = client.GetInfo(ctxt, infoReq) 49 require.Error(t, err) 50 require.Contains(t, err.Error(), "expected 1 macaroon") 51 }, 52 }, { 53 // Second test: Ensure that an invalid macaroon also triggers an 54 // error. 55 name: "invalid macaroon", 56 run: func(ctxt context.Context, t *testing.T) { 57 invalidMac, _ := macaroon.New( 58 []byte("dummy_root_key"), []byte("0"), "itest", 59 macaroon.LatestVersion, 60 ) 61 cleanup, client := macaroonClient( 62 t, testNode, invalidMac, 63 ) 64 defer cleanup() 65 _, err := client.GetInfo(ctxt, infoReq) 66 require.Error(t, err) 67 require.Contains(t, err.Error(), "cannot get macaroon") 68 }, 69 }, { 70 // Third test: Try to access a write method with read-only 71 // macaroon. 72 name: "read only macaroon", 73 run: func(ctxt context.Context, t *testing.T) { 74 readonlyMac, err := testNode.ReadMacaroon( 75 testNode.ReadMacPath(), defaultTimeout, 76 ) 77 require.NoError(t, err) 78 cleanup, client := macaroonClient( 79 t, testNode, readonlyMac, 80 ) 81 defer cleanup() 82 _, err = client.NewAddress(ctxt, newAddrReq) 83 require.Error(t, err) 84 require.Contains(t, err.Error(), "permission denied") 85 }, 86 }, { 87 // Fourth test: Check first-party caveat with timeout that 88 // expired 30 seconds ago. 89 name: "expired macaroon", 90 run: func(ctxt context.Context, t *testing.T) { 91 readonlyMac, err := testNode.ReadMacaroon( 92 testNode.ReadMacPath(), defaultTimeout, 93 ) 94 require.NoError(t, err) 95 timeoutMac, err := macaroons.AddConstraints( 96 readonlyMac, macaroons.TimeoutConstraint(-30), 97 ) 98 require.NoError(t, err) 99 cleanup, client := macaroonClient( 100 t, testNode, timeoutMac, 101 ) 102 defer cleanup() 103 _, err = client.GetInfo(ctxt, infoReq) 104 require.Error(t, err) 105 require.Contains(t, err.Error(), "macaroon has expired") 106 }, 107 }, { 108 // Fifth test: Check first-party caveat with invalid IP address. 109 name: "invalid IP macaroon", 110 run: func(ctxt context.Context, t *testing.T) { 111 readonlyMac, err := testNode.ReadMacaroon( 112 testNode.ReadMacPath(), defaultTimeout, 113 ) 114 require.NoError(t, err) 115 invalidIPAddrMac, err := macaroons.AddConstraints( 116 readonlyMac, macaroons.IPLockConstraint( 117 "1.1.1.1", 118 ), 119 ) 120 require.NoError(t, err) 121 cleanup, client := macaroonClient( 122 t, testNode, invalidIPAddrMac, 123 ) 124 defer cleanup() 125 _, err = client.GetInfo(ctxt, infoReq) 126 require.Error(t, err) 127 require.Contains(t, err.Error(), "different IP address") 128 }, 129 }, { 130 // Sixth test: Make sure that if we do everything correct and 131 // send the admin macaroon with first-party caveats that we can 132 // satisfy, we get a correct answer. 133 name: "correct macaroon", 134 run: func(ctxt context.Context, t *testing.T) { 135 adminMac, err := testNode.ReadMacaroon( 136 testNode.AdminMacPath(), defaultTimeout, 137 ) 138 require.NoError(t, err) 139 adminMac, err = macaroons.AddConstraints( 140 adminMac, macaroons.TimeoutConstraint(30), 141 macaroons.IPLockConstraint("127.0.0.1"), 142 ) 143 require.NoError(t, err) 144 cleanup, client := macaroonClient(t, testNode, adminMac) 145 defer cleanup() 146 res, err := client.NewAddress(ctxt, newAddrReq) 147 require.NoError(t, err, "get new address") 148 assert.Contains(t, res.Address, "Ss") 149 }, 150 }, { 151 // Seventh test: Bake a macaroon that can only access exactly 152 // two RPCs and make sure it works as expected. 153 name: "custom URI permissions", 154 run: func(ctxt context.Context, t *testing.T) { 155 entity := macaroons.PermissionEntityCustomURI 156 req := &lnrpc.BakeMacaroonRequest{ 157 Permissions: []*lnrpc.MacaroonPermission{{ 158 Entity: entity, 159 Action: "/lnrpc.Lightning/GetInfo", 160 }, { 161 Entity: entity, 162 Action: "/lnrpc.Lightning/List" + 163 "Permissions", 164 }}, 165 } 166 bakeRes, err := testNode.BakeMacaroon(ctxt, req) 167 require.NoError(t, err) 168 169 // Create a connection that uses the custom macaroon. 170 customMacBytes, err := hex.DecodeString( 171 bakeRes.Macaroon, 172 ) 173 require.NoError(t, err) 174 customMac := &macaroon.Macaroon{} 175 err = customMac.UnmarshalBinary(customMacBytes) 176 require.NoError(t, err) 177 cleanup, client := macaroonClient( 178 t, testNode, customMac, 179 ) 180 defer cleanup() 181 182 // Call GetInfo which should succeed. 183 _, err = client.GetInfo(ctxt, infoReq) 184 require.NoError(t, err) 185 186 // Call ListPermissions which should also succeed. 187 permReq := &lnrpc.ListPermissionsRequest{} 188 permRes, err := client.ListPermissions(ctxt, permReq) 189 require.NoError(t, err) 190 require.Greater( 191 t, len(permRes.MethodPermissions), 10, 192 "permissions", 193 ) 194 195 // Try NewAddress which should be denied. 196 _, err = client.NewAddress(ctxt, newAddrReq) 197 require.Error(t, err) 198 require.Contains(t, err.Error(), "permission denied") 199 }, 200 }, { 201 // Eighth test: check that with the CheckMacaroonPermissions 202 // RPC, we can check that a macaroon follows (or doesn't) 203 // permissions and constraints. 204 name: "unknown permissions", 205 run: func(ctxt context.Context, t *testing.T) { 206 // A test macaroon created with permissions from pool, 207 // to make sure CheckMacaroonPermissions RPC accepts 208 // them. 209 rootKeyID := uint64(4200) 210 req := &lnrpc.BakeMacaroonRequest{ 211 RootKeyId: rootKeyID, 212 Permissions: []*lnrpc.MacaroonPermission{{ 213 Entity: "account", 214 Action: "read", 215 }, { 216 Entity: "recommendation", 217 Action: "read", 218 }}, 219 AllowExternalPermissions: true, 220 } 221 bakeResp, err := testNode.BakeMacaroon(ctxt, req) 222 require.NoError(t, err) 223 224 macBytes, err := hex.DecodeString(bakeResp.Macaroon) 225 require.NoError(t, err) 226 227 checkReq := &lnrpc.CheckMacPermRequest{ 228 Macaroon: macBytes, 229 Permissions: req.Permissions, 230 } 231 232 // Test that CheckMacaroonPermissions accurately 233 // characterizes macaroon as valid, even if the 234 // permissions are not native to LND. 235 checkResp, err := testNode.CheckMacaroonPermissions( 236 ctxt, checkReq, 237 ) 238 require.NoError(t, err) 239 require.Equal(t, checkResp.Valid, true) 240 241 mac, err := readMacaroonFromHex(bakeResp.Macaroon) 242 require.NoError(t, err) 243 244 // Test that CheckMacaroonPermissions responds that the 245 // macaroon is invalid if timed out. 246 timeoutMac, err := macaroons.AddConstraints( 247 mac, macaroons.TimeoutConstraint(-30), 248 ) 249 require.NoError(t, err) 250 251 timeoutMacBytes, err := timeoutMac.MarshalBinary() 252 require.NoError(t, err) 253 254 checkReq.Macaroon = timeoutMacBytes 255 256 _, err = testNode.CheckMacaroonPermissions( 257 ctxt, checkReq, 258 ) 259 require.Error(t, err) 260 require.Contains(t, err.Error(), "macaroon has expired") 261 262 // Test that CheckMacaroonPermissions labels macaroon 263 // input with wrong permissions as invalid. 264 wrongPermissions := []*lnrpc.MacaroonPermission{{ 265 Entity: "invoice", 266 Action: "read", 267 }} 268 269 checkReq.Permissions = wrongPermissions 270 checkReq.Macaroon = macBytes 271 272 _, err = testNode.CheckMacaroonPermissions( 273 ctxt, checkReq, 274 ) 275 require.Error(t, err) 276 require.Contains(t, err.Error(), "permission denied") 277 }, 278 }} 279 280 for _, tc := range testCases { 281 tc := tc 282 ht.t.Run(tc.name, func(tt *testing.T) { 283 ctxt, cancel := context.WithTimeout( 284 context.Background(), defaultTimeout, 285 ) 286 defer cancel() 287 288 tc.run(ctxt, tt) 289 }) 290 } 291 } 292 293 // testBakeMacaroon checks that when creating macaroons, the permissions param 294 // in the request must be set correctly, and the baked macaroon has the intended 295 // permissions. 296 func testBakeMacaroon(net *lntest.NetworkHarness, t *harnessTest) { 297 var testNode = net.Alice 298 299 testCases := []struct { 300 name string 301 run func(ctxt context.Context, t *testing.T, 302 adminClient lnrpc.LightningClient) 303 }{{ 304 // First test: when the permission list is empty in the request, 305 // an error should be returned. 306 name: "no permission list", 307 run: func(ctxt context.Context, t *testing.T, 308 adminClient lnrpc.LightningClient) { 309 310 req := &lnrpc.BakeMacaroonRequest{} 311 _, err := adminClient.BakeMacaroon(ctxt, req) 312 require.Error(t, err) 313 assert.Contains( 314 t, err.Error(), "permission list cannot be "+ 315 "empty", 316 ) 317 }, 318 }, { 319 // Second test: when the action in the permission list is not 320 // valid, an error should be returned. 321 name: "invalid permission list", 322 run: func(ctxt context.Context, t *testing.T, 323 adminClient lnrpc.LightningClient) { 324 325 req := &lnrpc.BakeMacaroonRequest{ 326 Permissions: []*lnrpc.MacaroonPermission{{ 327 Entity: "macaroon", 328 Action: "invalid123", 329 }}, 330 } 331 _, err := adminClient.BakeMacaroon(ctxt, req) 332 require.Error(t, err) 333 assert.Contains( 334 t, err.Error(), "invalid permission action", 335 ) 336 }, 337 }, { 338 // Third test: when the entity in the permission list is not 339 // valid, an error should be returned. 340 name: "invalid permission entity", 341 run: func(ctxt context.Context, t *testing.T, 342 adminClient lnrpc.LightningClient) { 343 344 req := &lnrpc.BakeMacaroonRequest{ 345 Permissions: []*lnrpc.MacaroonPermission{{ 346 Entity: "invalid123", 347 Action: "read", 348 }}, 349 } 350 _, err := adminClient.BakeMacaroon(ctxt, req) 351 require.Error(t, err) 352 assert.Contains( 353 t, err.Error(), "invalid permission entity", 354 ) 355 }, 356 }, { 357 // Fourth test: check that when no root key ID is specified, the 358 // default root keyID is used. 359 name: "default root key ID", 360 run: func(ctxt context.Context, t *testing.T, 361 adminClient lnrpc.LightningClient) { 362 363 req := &lnrpc.BakeMacaroonRequest{ 364 Permissions: []*lnrpc.MacaroonPermission{{ 365 Entity: "macaroon", 366 Action: "read", 367 }}, 368 } 369 _, err := adminClient.BakeMacaroon(ctxt, req) 370 require.NoError(t, err) 371 372 listReq := &lnrpc.ListMacaroonIDsRequest{} 373 resp, err := adminClient.ListMacaroonIDs(ctxt, listReq) 374 require.NoError(t, err) 375 require.Equal(t, resp.RootKeyIds[0], uint64(0)) 376 }, 377 }, { 378 // Fifth test: create a macaroon use a non-default root key ID. 379 name: "custom root key ID", 380 run: func(ctxt context.Context, t *testing.T, 381 adminClient lnrpc.LightningClient) { 382 383 rootKeyID := uint64(4200) 384 req := &lnrpc.BakeMacaroonRequest{ 385 RootKeyId: rootKeyID, 386 Permissions: []*lnrpc.MacaroonPermission{{ 387 Entity: "macaroon", 388 Action: "read", 389 }}, 390 } 391 _, err := adminClient.BakeMacaroon(ctxt, req) 392 require.NoError(t, err) 393 394 listReq := &lnrpc.ListMacaroonIDsRequest{} 395 resp, err := adminClient.ListMacaroonIDs(ctxt, listReq) 396 require.NoError(t, err) 397 398 // the ListMacaroonIDs should give a list of two IDs, 399 // the default ID 0, and the newly created ID. The 400 // returned response is sorted to guarantee the order so 401 // that we can compare them one by one. 402 sort.Slice(resp.RootKeyIds, func(i, j int) bool { 403 return resp.RootKeyIds[i] < resp.RootKeyIds[j] 404 }) 405 require.Equal(t, resp.RootKeyIds[0], uint64(0)) 406 require.Equal(t, resp.RootKeyIds[1], rootKeyID) 407 }, 408 }, { 409 // Sixth test: check the baked macaroon has the intended 410 // permissions. It should succeed in reading, and fail to write 411 // a macaroon. 412 name: "custom macaroon permissions", 413 run: func(ctxt context.Context, t *testing.T, 414 adminClient lnrpc.LightningClient) { 415 416 rootKeyID := uint64(4200) 417 req := &lnrpc.BakeMacaroonRequest{ 418 RootKeyId: rootKeyID, 419 Permissions: []*lnrpc.MacaroonPermission{{ 420 Entity: "macaroon", 421 Action: "read", 422 }}, 423 } 424 bakeResp, err := adminClient.BakeMacaroon(ctxt, req) 425 require.NoError(t, err) 426 427 newMac, err := readMacaroonFromHex(bakeResp.Macaroon) 428 require.NoError(t, err) 429 cleanup, readOnlyClient := macaroonClient( 430 t, testNode, newMac, 431 ) 432 defer cleanup() 433 434 // BakeMacaroon requires a write permission, so this 435 // call should return an error. 436 _, err = readOnlyClient.BakeMacaroon(ctxt, req) 437 require.Error(t, err) 438 require.Contains(t, err.Error(), "permission denied") 439 440 // ListMacaroon requires a read permission, so this call 441 // should succeed. 442 listReq := &lnrpc.ListMacaroonIDsRequest{} 443 _, err = readOnlyClient.ListMacaroonIDs(ctxt, listReq) 444 require.NoError(t, err) 445 446 // Current macaroon can only work on entity macaroon, so 447 // a GetInfo request will fail. 448 infoReq := &lnrpc.GetInfoRequest{} 449 _, err = readOnlyClient.GetInfo(ctxt, infoReq) 450 require.Error(t, err) 451 require.Contains(t, err.Error(), "permission denied") 452 }, 453 }, { 454 // Seventh test: check that if the allow_external_permissions 455 // flag is set, we are able to feed BakeMacaroons permissions 456 // that LND is not familiar with. 457 name: "allow external macaroon permissions", 458 run: func(ctxt context.Context, t *testing.T, 459 adminClient lnrpc.LightningClient) { 460 461 // We'll try permissions from Pool to test that the 462 // allow_external_permissions flag properly allows it. 463 rootKeyID := uint64(4200) 464 req := &lnrpc.BakeMacaroonRequest{ 465 RootKeyId: rootKeyID, 466 Permissions: []*lnrpc.MacaroonPermission{{ 467 Entity: "account", 468 Action: "read", 469 }}, 470 AllowExternalPermissions: true, 471 } 472 473 resp, err := adminClient.BakeMacaroon(ctxt, req) 474 require.NoError(t, err) 475 476 // We'll also check that the external permission was 477 // successfully added to the macaroon. 478 macBytes, err := hex.DecodeString(resp.Macaroon) 479 require.NoError(t, err) 480 481 mac := &macaroon.Macaroon{} 482 err = mac.UnmarshalBinary(macBytes) 483 require.NoError(t, err) 484 485 rawID := mac.Id() 486 decodedID := &lnrpc.MacaroonId{} 487 idProto := rawID[1:] 488 err = proto.Unmarshal(idProto, decodedID) 489 require.NoError(t, err) 490 491 require.Equal(t, "account", decodedID.Ops[0].Entity) 492 require.Equal(t, "read", decodedID.Ops[0].Actions[0]) 493 }, 494 }} 495 496 for _, tc := range testCases { 497 tc := tc 498 t.t.Run(tc.name, func(tt *testing.T) { 499 ctxt, cancel := context.WithTimeout( 500 context.Background(), defaultTimeout, 501 ) 502 defer cancel() 503 504 adminMac, err := testNode.ReadMacaroon( 505 testNode.AdminMacPath(), defaultTimeout, 506 ) 507 require.NoError(tt, err) 508 cleanup, client := macaroonClient(tt, testNode, adminMac) 509 defer cleanup() 510 511 tc.run(ctxt, tt, client) 512 }) 513 } 514 } 515 516 // testDeleteMacaroonID checks that when deleting a macaroon ID, it removes the 517 // specified ID and invalidates all macaroons derived from the key with that ID. 518 // Also, it checks deleting the reserved marcaroon ID, DefaultRootKeyID or is 519 // forbidden. 520 func testDeleteMacaroonID(net *lntest.NetworkHarness, t *harnessTest) { 521 var ( 522 ctxb = context.Background() 523 testNode = net.Alice 524 ) 525 ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) 526 defer cancel() 527 528 // Use admin macaroon to create a connection. 529 adminMac, err := testNode.ReadMacaroon( 530 testNode.AdminMacPath(), defaultTimeout, 531 ) 532 require.NoError(t.t, err) 533 cleanup, client := macaroonClient(t.t, testNode, adminMac) 534 defer cleanup() 535 536 // Record the number of macaroon IDs before creation. 537 listReq := &lnrpc.ListMacaroonIDsRequest{} 538 listResp, err := client.ListMacaroonIDs(ctxt, listReq) 539 require.NoError(t.t, err) 540 numMacIDs := len(listResp.RootKeyIds) 541 542 // Create macaroons for testing. 543 rootKeyIDs := []uint64{1, 2, 3} 544 macList := make([]string, 0, len(rootKeyIDs)) 545 for _, id := range rootKeyIDs { 546 req := &lnrpc.BakeMacaroonRequest{ 547 RootKeyId: id, 548 Permissions: []*lnrpc.MacaroonPermission{{ 549 Entity: "macaroon", 550 Action: "read", 551 }}, 552 } 553 resp, err := client.BakeMacaroon(ctxt, req) 554 require.NoError(t.t, err) 555 macList = append(macList, resp.Macaroon) 556 } 557 558 // Check that the creation is successful. 559 listReq = &lnrpc.ListMacaroonIDsRequest{} 560 listResp, err = client.ListMacaroonIDs(ctxt, listReq) 561 require.NoError(t.t, err) 562 563 // The number of macaroon IDs should be increased by len(rootKeyIDs). 564 require.Equal(t.t, numMacIDs+len(rootKeyIDs), len(listResp.RootKeyIds)) 565 566 // First test: check deleting the DefaultRootKeyID returns an error. 567 defaultID, _ := strconv.ParseUint( 568 string(macaroons.DefaultRootKeyID), 10, 64, 569 ) 570 req := &lnrpc.DeleteMacaroonIDRequest{ 571 RootKeyId: defaultID, 572 } 573 _, err = client.DeleteMacaroonID(ctxt, req) 574 require.Error(t.t, err) 575 require.Contains( 576 t.t, err.Error(), macaroons.ErrDeletionForbidden.Error(), 577 ) 578 579 // Second test: check deleting the customized ID returns success. 580 req = &lnrpc.DeleteMacaroonIDRequest{ 581 RootKeyId: rootKeyIDs[0], 582 } 583 resp, err := client.DeleteMacaroonID(ctxt, req) 584 require.NoError(t.t, err) 585 require.True(t.t, resp.Deleted) 586 587 // Check that the deletion is successful. 588 listReq = &lnrpc.ListMacaroonIDsRequest{} 589 listResp, err = client.ListMacaroonIDs(ctxt, listReq) 590 require.NoError(t.t, err) 591 592 // The number of macaroon IDs should be decreased by 1. 593 require.Equal(t.t, numMacIDs+len(rootKeyIDs)-1, len(listResp.RootKeyIds)) 594 595 // Check that the deleted macaroon can no longer access macaroon:read. 596 deletedMac, err := readMacaroonFromHex(macList[0]) 597 require.NoError(t.t, err) 598 cleanup, client = macaroonClient(t.t, testNode, deletedMac) 599 defer cleanup() 600 601 // Because the macaroon is deleted, it will be treated as an invalid one. 602 listReq = &lnrpc.ListMacaroonIDsRequest{} 603 _, err = client.ListMacaroonIDs(ctxt, listReq) 604 require.Error(t.t, err) 605 require.Contains(t.t, err.Error(), "cannot get macaroon") 606 } 607 608 // testStatelessInit checks that the stateless initialization of the daemon 609 // does not write any macaroon files to the daemon's file system and returns 610 // the admin macaroon in the response. It then checks that the password 611 // change of the wallet can also happen stateless. 612 func testStatelessInit(net *lntest.NetworkHarness, t *harnessTest) { 613 var ( 614 initPw = []byte("stateless") 615 newPw = []byte("stateless-new") 616 newAddrReq = &lnrpc.NewAddressRequest{ 617 Type: AddrTypePubkeyHash, 618 } 619 ) 620 621 // First, create a new node and request it to initialize stateless. 622 // This should return us the binary serialized admin macaroon that we 623 // can then use for further calls. 624 carol, _, macBytes, err := net.NewNodeWithSeed( 625 "Carol", nil, initPw, true, 626 ) 627 require.NoError(t.t, err) 628 if len(macBytes) == 0 { 629 t.Fatalf("invalid macaroon returned in stateless init") 630 } 631 632 // Now make sure no macaroon files have been created by the node Carol. 633 _, err = os.Stat(carol.AdminMacPath()) 634 require.Error(t.t, err) 635 _, err = os.Stat(carol.ReadMacPath()) 636 require.Error(t.t, err) 637 _, err = os.Stat(carol.InvoiceMacPath()) 638 require.Error(t.t, err) 639 640 // Then check that we can unmarshal the binary serialized macaroon. 641 adminMac := &macaroon.Macaroon{} 642 err = adminMac.UnmarshalBinary(macBytes) 643 require.NoError(t.t, err) 644 645 // Find out if we can actually use the macaroon that has been returned 646 // to us for a RPC call. 647 conn, err := carol.ConnectRPCWithMacaroon(adminMac) 648 require.NoError(t.t, err) 649 defer conn.Close() 650 adminMacClient := lnrpc.NewLightningClient(conn) 651 ctxt, _ := context.WithTimeout(context.Background(), defaultTimeout) 652 res, err := adminMacClient.NewAddress(ctxt, newAddrReq) 653 require.NoError(t.t, err) 654 if _, err := stdaddr.DecodeAddressV0(res.Address, harnessNetParams); err != nil { 655 t.Fatalf("returned address was not a regtest address") 656 } 657 658 if carol.Cfg.RemoteWallet { 659 t.Log("Skipping rest of test because RemoteWallet does not support " + 660 "changing password") 661 return 662 } 663 664 // As a second part, shut down the node and then try to change the 665 // password when we start it up again. 666 if err := net.RestartNodeNoUnlock(carol, nil, true); err != nil { 667 t.Fatalf("Node restart failed: %v", err) 668 } 669 changePwReq := &lnrpc.ChangePasswordRequest{ 670 CurrentPassword: initPw, 671 NewPassword: newPw, 672 StatelessInit: true, 673 } 674 response, err := carol.InitChangePassword(changePwReq) 675 require.NoError(t.t, err) 676 677 // Again, make sure no macaroon files have been created by the node 678 // Carol. 679 _, err = os.Stat(carol.AdminMacPath()) 680 require.Error(t.t, err) 681 _, err = os.Stat(carol.ReadMacPath()) 682 require.Error(t.t, err) 683 _, err = os.Stat(carol.InvoiceMacPath()) 684 require.Error(t.t, err) 685 686 // Then check that we can unmarshal the new binary serialized macaroon 687 // and that it really is a new macaroon. 688 if err = adminMac.UnmarshalBinary(response.AdminMacaroon); err != nil { 689 t.Fatalf("unable to unmarshal macaroon: %v", err) 690 } 691 if bytes.Equal(response.AdminMacaroon, macBytes) { 692 t.Fatalf("expected new macaroon to be different") 693 } 694 695 // Finally, find out if we can actually use the new macaroon that has 696 // been returned to us for a RPC call. 697 conn2, err := carol.ConnectRPCWithMacaroon(adminMac) 698 require.NoError(t.t, err) 699 defer conn2.Close() 700 adminMacClient = lnrpc.NewLightningClient(conn2) 701 702 // Changing the password takes a while, so we use the default timeout 703 // of 30 seconds to wait for the connection to be ready. 704 ctxt, _ = context.WithTimeout(context.Background(), defaultTimeout) 705 res, err = adminMacClient.NewAddress(ctxt, newAddrReq) 706 require.NoError(t.t, err) 707 if _, err := stdaddr.DecodeAddressV0(res.Address, harnessNetParams); err != nil { 708 t.Fatalf("returned address was not a regtest address") 709 } 710 } 711 712 // readMacaroonFromHex loads a macaroon from a hex string. 713 func readMacaroonFromHex(macHex string) (*macaroon.Macaroon, error) { 714 macBytes, err := hex.DecodeString(macHex) 715 if err != nil { 716 return nil, err 717 } 718 719 mac := &macaroon.Macaroon{} 720 if err := mac.UnmarshalBinary(macBytes); err != nil { 721 return nil, err 722 } 723 return mac, nil 724 } 725 726 func macaroonClient(t *testing.T, testNode *lntest.HarnessNode, 727 mac *macaroon.Macaroon) (func(), lnrpc.LightningClient) { 728 729 conn, err := testNode.ConnectRPCWithMacaroon(mac) 730 require.NoError(t, err, "connect to alice") 731 732 cleanup := func() { 733 err := conn.Close() 734 require.NoError(t, err, "close") 735 } 736 return cleanup, lnrpc.NewLightningClient(conn) 737 }