Skip to content

Commit fde1b57

Browse files
committed
test(solana/e2e): add access manager transfer tests for all programs
- ICS26 Router: propose/accept/cancel + negative (non-admin) test - ICS07 Tendermint: propose/accept/cancel flow - ICS27 GMP: propose/accept/cancel flow - Attestation: propose/accept/cancel flow
1 parent 336efca commit fde1b57

File tree

4 files changed

+737
-46
lines changed

4 files changed

+737
-46
lines changed

e2e/interchaintestv8/solana_attestation_test.go

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,6 +1277,176 @@ func convertSolanaPacketToABI(packet solana.SolanaPacket) ics26router.IICS26Rout
12771277
}
12781278
}
12791279

1280+
// Test_Attestation_AccessManagerTransfer tests propose/accept/cancel access manager
1281+
// transfer on the attestation light client program.
1282+
func (s *IbcSolanaAttestationTestSuite) Test_Attestation_AccessManagerTransfer() {
1283+
ctx := context.Background()
1284+
s.SetupSuite(ctx)
1285+
1286+
const keypairDir = "solana-keypairs/localnet"
1287+
const deployerPath = keypairDir + "/deployer_wallet.json"
1288+
1289+
// --- Deploy and initialize AM-B ---
1290+
1291+
var amBProgramID solanago.PublicKey
1292+
1293+
s.Require().True(s.Run("Deploy AM-B (test_access_manager)", func() {
1294+
var err error
1295+
amBKeypairPath := fmt.Sprintf("%s/test_access_manager-keypair.json", keypairDir)
1296+
amBProgramID, err = s.Solana.Chain.DeploySolanaProgramAsync(ctx, "test_access_manager", amBKeypairPath, deployerPath)
1297+
s.Require().NoError(err, "failed to deploy test_access_manager")
1298+
}))
1299+
1300+
s.Require().True(s.Run("Initialize AM-B with user as admin", func() {
1301+
deployerWallet, err := solana.LoadDeployerWallet(deployerPath)
1302+
s.Require().NoError(err)
1303+
1304+
amBAccessManagerPDA, _ := solana.AccessManager.AccessManagerPDA(amBProgramID)
1305+
amBProgramDataPDA, err := solana.GetProgramDataAddress(amBProgramID)
1306+
s.Require().NoError(err)
1307+
1308+
savedProgramID := access_manager.ProgramID
1309+
access_manager.ProgramID = amBProgramID
1310+
defer func() { access_manager.ProgramID = savedProgramID }()
1311+
1312+
initIx, err := access_manager.NewInitializeInstruction(
1313+
s.SolanaUser.PublicKey(),
1314+
amBAccessManagerPDA,
1315+
s.SolanaUser.PublicKey(),
1316+
solanago.SystemProgramID,
1317+
solanago.SysVarInstructionsPubkey,
1318+
amBProgramDataPDA,
1319+
solana.DeployerPubkey,
1320+
)
1321+
s.Require().NoError(err)
1322+
1323+
tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaUser.PublicKey(), initIx)
1324+
s.Require().NoError(err)
1325+
1326+
_, err = s.Solana.Chain.SignAndBroadcastTxWithRetryAndTimeout(ctx, tx, rpc.CommitmentConfirmed, 30, s.SolanaUser, deployerWallet)
1327+
s.Require().NoError(err, "failed to initialize AM-B")
1328+
}))
1329+
1330+
// --- Helper: read attestation app state ---
1331+
1332+
appStatePDA, _ := solana.Attestation.AppStatePDA(attestation.ProgramID)
1333+
1334+
readAppState := func() *attestation.AttestationTypesAppState {
1335+
s.T().Helper()
1336+
accountInfo, err := s.Solana.Chain.RPCClient.GetAccountInfoWithOpts(ctx, appStatePDA, &rpc.GetAccountInfoOpts{
1337+
Commitment: rpc.CommitmentConfirmed,
1338+
})
1339+
s.Require().NoError(err)
1340+
s.Require().NotNil(accountInfo.Value)
1341+
state, err := attestation.ParseAccount_AttestationTypesAppState(accountInfo.Value.Data.GetBinary())
1342+
s.Require().NoError(err)
1343+
return state
1344+
}
1345+
1346+
amAAccessManagerPDA, _ := solana.AccessManager.AccessManagerPDA(access_manager.ProgramID)
1347+
1348+
// --- Verify initial state ---
1349+
1350+
s.Require().True(s.Run("Verify initial state: AM-A is active, no pending", func() {
1351+
state := readAppState()
1352+
s.Require().Equal(access_manager.ProgramID, state.AccessManager, "Attestation should point to AM-A")
1353+
s.Require().Nil(state.PendingAccessManager, "No pending transfer initially")
1354+
}))
1355+
1356+
// --- Propose transfer to AM-B ---
1357+
1358+
s.Require().True(s.Run("Propose access manager transfer to AM-B", func() {
1359+
proposeIx, err := attestation.NewProposeAccessManagerTransferInstruction(
1360+
amBProgramID,
1361+
appStatePDA,
1362+
amAAccessManagerPDA,
1363+
s.SolanaUser.PublicKey(),
1364+
solanago.SysVarInstructionsPubkey,
1365+
)
1366+
s.Require().NoError(err)
1367+
1368+
tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaUser.PublicKey(), proposeIx)
1369+
s.Require().NoError(err)
1370+
1371+
_, err = s.Solana.Chain.SignAndBroadcastTxWithRetry(ctx, tx, rpc.CommitmentConfirmed, s.SolanaUser)
1372+
s.Require().NoError(err, "propose should succeed")
1373+
}))
1374+
1375+
s.Require().True(s.Run("Verify: pending set, AM unchanged", func() {
1376+
state := readAppState()
1377+
s.Require().Equal(access_manager.ProgramID, state.AccessManager, "AM should still be AM-A")
1378+
s.Require().NotNil(state.PendingAccessManager, "Pending should be set")
1379+
s.Require().Equal(amBProgramID, *state.PendingAccessManager, "Pending should be AM-B")
1380+
}))
1381+
1382+
// --- Accept transfer ---
1383+
1384+
amBAccessManagerPDA, _ := solana.AccessManager.AccessManagerPDA(amBProgramID)
1385+
1386+
s.Require().True(s.Run("Accept access manager transfer (AM-B admin)", func() {
1387+
acceptIx, err := attestation.NewAcceptAccessManagerTransferInstruction(
1388+
appStatePDA,
1389+
amBAccessManagerPDA,
1390+
s.SolanaUser.PublicKey(),
1391+
solanago.SysVarInstructionsPubkey,
1392+
)
1393+
s.Require().NoError(err)
1394+
1395+
tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaUser.PublicKey(), acceptIx)
1396+
s.Require().NoError(err)
1397+
1398+
_, err = s.Solana.Chain.SignAndBroadcastTxWithRetry(ctx, tx, rpc.CommitmentConfirmed, s.SolanaUser)
1399+
s.Require().NoError(err, "accept should succeed")
1400+
}))
1401+
1402+
s.Require().True(s.Run("Verify: AM is now AM-B, pending cleared", func() {
1403+
state := readAppState()
1404+
s.Require().Equal(amBProgramID, state.AccessManager, "AM should now be AM-B")
1405+
s.Require().Nil(state.PendingAccessManager, "Pending should be cleared after accept")
1406+
}))
1407+
1408+
// --- Propose back to AM-A and cancel ---
1409+
1410+
s.Require().True(s.Run("Propose transfer back to AM-A", func() {
1411+
proposeIx, err := attestation.NewProposeAccessManagerTransferInstruction(
1412+
access_manager.ProgramID,
1413+
appStatePDA,
1414+
amBAccessManagerPDA,
1415+
s.SolanaUser.PublicKey(),
1416+
solanago.SysVarInstructionsPubkey,
1417+
)
1418+
s.Require().NoError(err)
1419+
1420+
tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaUser.PublicKey(), proposeIx)
1421+
s.Require().NoError(err)
1422+
1423+
_, err = s.Solana.Chain.SignAndBroadcastTxWithRetry(ctx, tx, rpc.CommitmentConfirmed, s.SolanaUser)
1424+
s.Require().NoError(err, "propose back to AM-A should succeed")
1425+
}))
1426+
1427+
s.Require().True(s.Run("Cancel pending transfer", func() {
1428+
cancelIx, err := attestation.NewCancelAccessManagerTransferInstruction(
1429+
appStatePDA,
1430+
amBAccessManagerPDA,
1431+
s.SolanaUser.PublicKey(),
1432+
solanago.SysVarInstructionsPubkey,
1433+
)
1434+
s.Require().NoError(err)
1435+
1436+
tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaUser.PublicKey(), cancelIx)
1437+
s.Require().NoError(err)
1438+
1439+
_, err = s.Solana.Chain.SignAndBroadcastTxWithRetry(ctx, tx, rpc.CommitmentConfirmed, s.SolanaUser)
1440+
s.Require().NoError(err, "cancel should succeed")
1441+
}))
1442+
1443+
s.Require().True(s.Run("Verify: pending cleared, AM still AM-B", func() {
1444+
state := readAppState()
1445+
s.Require().Equal(amBProgramID, state.AccessManager, "AM should still be AM-B")
1446+
s.Require().Nil(state.PendingAccessManager, "Pending should be cleared after cancel")
1447+
}))
1448+
}
1449+
12801450
// deriveAttestationConsensusStatePDA fetches the attestation client state to get the latest height,
12811451
// then derives the consensus state PDA.
12821452
func (s *IbcSolanaAttestationTestSuite) deriveAttestationConsensusStatePDA(ctx context.Context, clientStatePDA solanago.PublicKey) solanago.PublicKey {

0 commit comments

Comments
 (0)