diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index b4bc1f36..3cd9abaf 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -120,11 +120,11 @@ jobs: fail-fast: false matrix: test_type: [ - "Cork", "ScheduledCork", - "AuctionModule", + "Auction", "CellarFees", "Incentives", + "Pubsub", ] steps: - name: Set up Go 1.19 diff --git a/Makefile b/Makefile index 3c8cdefe..5720b6df 100644 --- a/Makefile +++ b/Makefile @@ -369,7 +369,7 @@ e2e_scheduled_cork_test: e2e_clean_slate @E2E_SKIP_CLEANUP=true integration_tests/integration_tests.test -test.failfast -test.v -test.run IntegrationTestSuite -testify.m TestScheduledCork || make -s fail e2e_auction_module_test: e2e_clean_slate - @E2E_SKIP_CLEANUP=true integration_tests/integration_tests.test -test.failfast -test.v -test.run IntegrationTestSuite -testify.m TestAuctionModule || make -s fail + @E2E_SKIP_CLEANUP=true integration_tests/integration_tests.test -test.failfast -test.v -test.run IntegrationTestSuite -testify.m TestAuction || make -s fail e2e_cellarfees_test: e2e_clean_slate @E2E_SKIP_CLEANUP=true integration_tests/integration_tests.test -test.failfast -test.v -test.run IntegrationTestSuite -testify.m TestCellarFees || make -s fail @@ -377,6 +377,9 @@ e2e_cellarfees_test: e2e_clean_slate e2e_incentives_test: e2e_clean_slate @E2E_SKIP_CLEANUP=true integration_tests/integration_tests.test -test.failfast -test.v -test.run IntegrationTestSuite -testify.m TestIncentives || make -s fail +e2e_pubsub_test: e2e_clean_slate + @E2E_SKIP_CLEANUP=true integration_tests/integration_tests.test -test.failfast -test.v -test.run IntegrationTestSuite -testify.m TestPubsub || make -s fail + fail: @echo 'test failed; dumping container logs into ./testlogs for review' @docker logs ethereum > testlogs/ethereum.log 2>&1 || true diff --git a/integration_tests/auction_test.go b/integration_tests/auction_test.go index 9e19a3be..51ead9b6 100644 --- a/integration_tests/auction_test.go +++ b/integration_tests/auction_test.go @@ -12,7 +12,7 @@ import ( cellarfees "github.com/peggyjv/sommelier/v7/x/cellarfees/types" ) -func (s *IntegrationTestSuite) TestAuctionModule() { +func (s *IntegrationTestSuite) TestAuction() { s.Run("Bring up chain, test governance proposal to set token prices, submit some bids, and finish an auction", func() { val := s.chain.validators[0] kb, err := val.keyring() diff --git a/integration_tests/cork_test.go b/integration_tests/cork_test.go deleted file mode 100644 index 275d81a6..00000000 --- a/integration_tests/cork_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package integration_tests - -import ( - "context" - "fmt" - "strings" - - errorsmod "cosmossdk.io/errors" - "cosmossdk.io/math" - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethclient" -) - -const CounterABI = ` - [ - { - "inputs": [], - "name": "count", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "dec", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "get", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "inc", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } - ] -` - -func ABIEncodedGet() []byte { - encodedCall, err := abi.JSON(strings.NewReader(CounterABI)) - if err != nil { - panic(errorsmod.Wrap(err, "bad ABI definition in code")) - } - - abiEncodedCall, err := encodedCall.Pack("get") - if err != nil { - panic(err) - } - - return abiEncodedCall -} - -func ABIEncodedInc() []byte { - encodedCall, err := abi.JSON(strings.NewReader(CounterABI)) - if err != nil { - panic(errorsmod.Wrap(err, "bad ABI definition in code")) - } - - abiEncodedCall, err := encodedCall.Pack("inc") - if err != nil { - panic(err) - } - - return abiEncodedCall -} - -func (s *IntegrationTestSuite) getCurrentCount() (*math.Int, error) { - ethClient, err := ethclient.Dial(fmt.Sprintf("http://%s", s.ethResource.GetHostPort("8545/tcp"))) - if err != nil { - return nil, err - } - - bz, err := ethClient.CallContract(context.Background(), ethereum.CallMsg{ - From: common.HexToAddress(s.chain.validators[0].ethereumKey.address), - To: &counterContract, - Gas: 0, - Data: ABIEncodedGet(), - }, nil) - if err != nil { - return nil, err - } - - count := UnpackEthUInt(bz) - - return &count, nil -} diff --git a/integration_tests/pubsub_test.go b/integration_tests/pubsub_test.go new file mode 100644 index 00000000..902b30e7 --- /dev/null +++ b/integration_tests/pubsub_test.go @@ -0,0 +1,605 @@ +package integration_tests + +import ( + "context" + "fmt" + "time" + + "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" + govtypesv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/peggyjv/sommelier/v7/x/pubsub/types" +) + +func (s *IntegrationTestSuite) TestPubsub() { + s.Run("Test the pubsub module", func() { + // Set up validator, orchestrator, proposer, query client + val0 := s.chain.validators[0] + val0kb, err := val0.keyring() + s.Require().NoError(err) + val0ClientCtx, err := s.chain.clientContext("tcp://localhost:26657", &val0kb, "val", val0.address()) + s.Require().NoError(err) + + orch0 := s.chain.orchestrators[0] + orch0ClientCtx, err := s.chain.clientContext("tcp://localhost:26657", orch0.keyring, "orch", orch0.address()) + s.Require().NoError(err) + orch1 := s.chain.orchestrators[1] + orch1ClientCtx, err := s.chain.clientContext("tcp://localhost:26657", orch1.keyring, "orch", orch1.address()) + s.Require().NoError(err) + + proposer := s.chain.proposer + proposerCtx, err := s.chain.clientContext("tcp://localhost:26657", proposer.keyring, "proposer", proposer.address()) + s.Require().NoError(err) + propID := uint64(1) + + pubsubQueryClient := types.NewQueryClient(val0ClientCtx) + + //////////////// + // Happy path // + //////////////// + + // add publisher (controlled by proposer) + s.T().Log("Creating AddPublisherProposal") + addPublisherProp := types.AddPublisherProposal{ + Title: "add a publisher", + Description: "example publisher", + Domain: "example.com", + Address: proposer.address().String(), + ProofUrl: fmt.Sprintf("https://example.com/%s/cacert.pem", proposer.address().String()), + CaCert: PublisherCACert, + } + + addPublisherPropMsg, err := govtypesv1beta1.NewMsgSubmitProposal( + &addPublisherProp, + sdk.Coins{ + { + Denom: testDenom, + Amount: math.NewInt(2), + }, + }, + proposer.address(), + ) + s.Require().NoError(err, "Unable to create governance proposal") + + s.submitAndVoteForProposal(proposerCtx, orch0ClientCtx, propID, addPublisherPropMsg) + propID++ + + s.T().Log("Verifying Publisher correctly added") + publishersResponse, err := pubsubQueryClient.QueryPublishers(context.Background(), &types.QueryPublishersRequest{}) + s.Require().NoError(err) + s.Require().Len(publishersResponse.Publishers, 1) + publisher := publishersResponse.Publishers[0] + s.Require().Equal(publisher.Address, proposer.address().String()) + s.Require().Equal(publisher.CaCert, PublisherCACert) + s.Require().Equal(publisher.Domain, "example.com") + + // set publisher intent for cellar + s.T().Log("Submitting AddPublisherIntent") + subscriptionID := fmt.Sprintf("1:%s", unusedGenesisContract.String()) + addPublisherIntentMsg := types.MsgAddPublisherIntentRequest{ + PublisherIntent: &types.PublisherIntent{ + SubscriptionId: subscriptionID, + PublisherDomain: publisher.Domain, + Method: types.PublishMethod_PUSH, + AllowedSubscribers: types.AllowedSubscribers_VALIDATORS, + }, + Signer: proposer.address().String(), + } + + _, err = s.chain.sendMsgs(*proposerCtx, &addPublisherIntentMsg) + s.Require().NoError(err) + s.T().Log("AddPublisherIntent submitted successfully") + + s.T().Log("Verifying PublisherIntent correctly added") + publisherIntentsResponse, err := pubsubQueryClient.QueryPublisherIntents(context.Background(), &types.QueryPublisherIntentsRequest{}) + s.Require().NoError(err) + s.Require().Len(publisherIntentsResponse.PublisherIntents, 1) + publisherIntent := publisherIntentsResponse.PublisherIntents[0] + s.Require().Equal(publisherIntent.SubscriptionId, subscriptionID) + s.Require().Equal(publisherIntent.PublisherDomain, publisher.Domain) + s.Require().Equal(publisherIntent.Method, types.PublishMethod_PUSH) + s.Require().Equal(publisherIntent.AllowedSubscribers, types.AllowedSubscribers_VALIDATORS) + + // add default subscription prop + s.T().Log("Creating AddDefaultSubscriptionProposal") + addDefaultSubscriptionProp := types.AddDefaultSubscriptionProposal{ + Title: "add a default subscription", + Description: "a default subscription!", + SubscriptionId: subscriptionID, + PublisherDomain: publisher.Domain, + } + + addDefaultSubscriptionPropMsg, err := govtypesv1beta1.NewMsgSubmitProposal( + &addDefaultSubscriptionProp, + sdk.Coins{ + { + Denom: testDenom, + Amount: math.NewInt(2), + }, + }, + proposer.address(), + ) + s.Require().NoError(err, "Unable to create governance proposal") + + s.submitAndVoteForProposal(proposerCtx, orch0ClientCtx, propID, addDefaultSubscriptionPropMsg) + propID++ + + s.T().Log("Verifying DefaultSubscription correctly added") + defaultSubscriptionsResponse, err := pubsubQueryClient.QueryDefaultSubscriptions(context.Background(), &types.QueryDefaultSubscriptionsRequest{}) + s.Require().NoError(err) + s.Require().Len(defaultSubscriptionsResponse.DefaultSubscriptions, 1) + defaultSubscription := defaultSubscriptionsResponse.DefaultSubscriptions[0] + s.Require().Equal(defaultSubscription.SubscriptionId, subscriptionID) + s.Require().Equal(defaultSubscription.PublisherDomain, publisher.Domain) + + // create subscribers + s.T().Log("Creating Subscriber for two orchestrators") + subscriber0PushURL := "https://steward.orch0.example.com:5734" + addSubscriber0Msg := types.MsgAddSubscriberRequest{ + Subscriber: &types.Subscriber{ + Address: orch0.address().String(), + CaCert: SubscriberCACert, + PushUrl: subscriber0PushURL, + }, + Signer: orch0.address().String(), + } + + subscriber1PushURL := "https://steward.orch1.example.com:5734" + addSubscriber1Msg := types.MsgAddSubscriberRequest{ + Subscriber: &types.Subscriber{ + Address: orch1.address().String(), + CaCert: SubscriberCACert, + PushUrl: subscriber1PushURL, + }, + Signer: orch1.address().String(), + } + + _, err = s.chain.sendMsgs(*orch0ClientCtx, &addSubscriber0Msg) + s.Require().NoError(err) + s.T().Log("AddSubscriber for orch 0 submitted correctly") + + _, err = s.chain.sendMsgs(*orch1ClientCtx, &addSubscriber1Msg) + s.Require().NoError(err) + s.T().Log("AddSubscriber for orch 1 submitted correctly") + + s.T().Log("Verifying Subscribers added correctly") + subscribersResponse, err := pubsubQueryClient.QuerySubscribers(context.Background(), &types.QuerySubscribersRequest{}) + s.Require().NoError(err) + s.Require().Len(subscribersResponse.Subscribers, 2) + + subscriber0 := subscribersResponse.Subscribers[0] + subscriber1 := subscribersResponse.Subscribers[1] + s.Require().Equal(subscriber0.Address, orch0.address().String()) + s.Require().Equal(subscriber0.CaCert, SubscriberCACert) + s.Require().Equal(subscriber0.PushUrl, subscriber0PushURL) + s.Require().Equal(subscriber1.Address, orch1.address().String()) + s.Require().Equal(subscriber1.CaCert, SubscriberCACert) + s.Require().Equal(subscriber1.PushUrl, subscriber1PushURL) + + // subscribe to the cellar + s.T().Log("Creating SubscriberIntent for both orchestrators") + addSubscriberIntent0Msg := types.MsgAddSubscriberIntentRequest{ + SubscriberIntent: &types.SubscriberIntent{ + SubscriptionId: subscriptionID, + SubscriberAddress: orch0.address().String(), + PublisherDomain: publisher.Domain, + }, + Signer: orch0.address().String(), + } + + addSubscriberIntent1Msg := types.MsgAddSubscriberIntentRequest{ + SubscriberIntent: &types.SubscriberIntent{ + SubscriptionId: subscriptionID, + SubscriberAddress: orch1.address().String(), + PublisherDomain: publisher.Domain, + }, + Signer: orch1.address().String(), + } + + // subscriber 1 should not be able to add a subscriber intent for subscriber 0 + _, err = s.chain.sendMsgs(*orch1ClientCtx, &addSubscriberIntent0Msg) + s.Require().NoError(err) + s.T().Log("AddSubscriberIntent for orch 0 submitted incorrectly, verifying none created") + subscriberIntentsResponse, err := pubsubQueryClient.QuerySubscriberIntents(context.Background(), &types.QuerySubscriberIntentsRequest{}) + s.Require().NoError(err) + s.Require().Len(subscriberIntentsResponse.SubscriberIntents, 0) + + _, err = s.chain.sendMsgs(*orch0ClientCtx, &addSubscriberIntent0Msg) + s.Require().NoError(err) + s.T().Log("AddSubscriberIntent for orch 0 submitted correctly") + + _, err = s.chain.sendMsgs(*orch1ClientCtx, &addSubscriberIntent1Msg) + s.Require().NoError(err) + s.T().Log("AddSubscriberIntent for orch 1 submitted correctly") + + s.T().Log("Verifying SubscriberIntents added correctly") + subscriberIntentsResponse, err = pubsubQueryClient.QuerySubscriberIntents(context.Background(), &types.QuerySubscriberIntentsRequest{}) + s.Require().NoError(err) + s.Require().Len(subscriberIntentsResponse.SubscriberIntents, 2) + + subscriberIntent0 := subscriberIntentsResponse.SubscriberIntents[0] + subscriberIntent1 := subscriberIntentsResponse.SubscriberIntents[1] + s.Require().Equal(subscriberIntent0.SubscriptionId, subscriptionID) + s.Require().Equal(subscriberIntent0.SubscriberAddress, orch0.address().String()) + s.Require().Equal(subscriberIntent0.PublisherDomain, publisher.Domain) + s.Require().Equal(subscriberIntent1.SubscriptionId, subscriptionID) + s.Require().Equal(subscriberIntent1.SubscriberAddress, orch1.address().String()) + s.Require().Equal(subscriberIntent1.PublisherDomain, publisher.Domain) + + // remove subscriptions to the cellar + s.T().Log("Removing SubscriberIntent for orch 0") + removeSubscriberIntent0Msg := types.MsgRemoveSubscriberIntentRequest{ + SubscriptionId: subscriptionID, + SubscriberAddress: orch0.address().String(), + Signer: orch0.address().String(), + } + + _, err = s.chain.sendMsgs(*orch0ClientCtx, &removeSubscriberIntent0Msg) + s.Require().NoError(err) + s.T().Log("RemoveSubscriberIntent for orch 0 submitted correctly") + + s.T().Log("Verifying SubscriberIntent for orch 0 removed") + subscriberIntentsResponse, err = pubsubQueryClient.QuerySubscriberIntents(context.Background(), &types.QuerySubscriberIntentsRequest{}) + s.Require().NoError(err) + s.Require().Len(subscriberIntentsResponse.SubscriberIntents, 1) + s.Require().Equal(subscriberIntentsResponse.SubscriberIntents[0].SubscriberAddress, orch1.address().String()) + + s.T().Log("Removing SubscriberIntent for orch 1") + removeSubscriberIntent1Msg := types.MsgRemoveSubscriberIntentRequest{ + SubscriptionId: subscriptionID, + SubscriberAddress: orch1.address().String(), + Signer: orch1.address().String(), + } + + _, err = s.chain.sendMsgs(*orch1ClientCtx, &removeSubscriberIntent1Msg) + s.Require().NoError(err) + s.T().Log("RemoveSubscriberIntent for orch 1 submitted correctly") + + s.T().Log("Verifying SubscriberIntent for orch 1 removed") + subscriberIntentsResponse, err = pubsubQueryClient.QuerySubscriberIntents(context.Background(), &types.QuerySubscriberIntentsRequest{}) + s.Require().NoError(err) + s.Require().Len(subscriberIntentsResponse.SubscriberIntents, 0) + + // delete subscribers + s.T().Log("Removing Subscriber for orch 0") + removeSubscriber0Msg := types.MsgRemoveSubscriberRequest{ + SubscriberAddress: orch0.address().String(), + Signer: orch0.address().String(), + } + + _, err = s.chain.sendMsgs(*orch0ClientCtx, &removeSubscriber0Msg) + s.Require().NoError(err) + s.T().Log("RemoveSubscriber for orch 0 submitted correctly") + + s.T().Log("Verifying Subscriber for orch 0 removed") + subscribersResponse, err = pubsubQueryClient.QuerySubscribers(context.Background(), &types.QuerySubscribersRequest{}) + s.Require().NoError(err) + s.Require().Len(subscribersResponse.Subscribers, 1) + s.Require().Equal(subscribersResponse.Subscribers[0].Address, orch1.address().String()) + + s.T().Log("Removing Subscriber for orch 1") + removeSubscriber1Msg := types.MsgRemoveSubscriberRequest{ + SubscriberAddress: orch1.address().String(), + Signer: orch1.address().String(), + } + + _, err = s.chain.sendMsgs(*orch1ClientCtx, &removeSubscriber1Msg) + s.Require().NoError(err) + s.T().Log("RemoveSubscriber for orch 1 submitted correctly") + + s.T().Log("Verifying Subscriber for orch 1 removed") + subscribersResponse, err = pubsubQueryClient.QuerySubscribers(context.Background(), &types.QuerySubscribersRequest{}) + s.Require().NoError(err) + s.Require().Len(subscribersResponse.Subscribers, 0) + + // remove default subscription prop + s.T().Log("Creating RemoveDefaultSubscriptionProposal") + removeDefaultSubscriptionProp := types.RemoveDefaultSubscriptionProposal{ + Title: "remove a default subscription", + Description: "a default subscription is being removed!", + SubscriptionId: subscriptionID, + } + + removeDefaultSubscriptionPropMsg, err := govtypesv1beta1.NewMsgSubmitProposal( + &removeDefaultSubscriptionProp, + sdk.Coins{ + { + Denom: testDenom, + Amount: math.NewInt(2), + }, + }, + proposer.address(), + ) + s.Require().NoError(err, "Unable to create governance proposal") + + s.submitAndVoteForProposal(proposerCtx, orch0ClientCtx, propID, removeDefaultSubscriptionPropMsg) + propID++ + + s.T().Log("Verifying DefaultSubscription correctly removed") + defaultSubscriptionsResponse, err = pubsubQueryClient.QueryDefaultSubscriptions(context.Background(), &types.QueryDefaultSubscriptionsRequest{}) + s.Require().NoError(err) + s.Require().Len(defaultSubscriptionsResponse.DefaultSubscriptions, 0) + + // remove publisher intent for cellar + s.T().Log("Removing PublisherIntent") + removePublisherIntentMsg := types.MsgRemovePublisherIntentRequest{ + SubscriptionId: subscriptionID, + PublisherDomain: publisher.Domain, + Signer: proposer.address().String(), + } + + _, err = s.chain.sendMsgs(*proposerCtx, &removePublisherIntentMsg) + s.Require().NoError(err) + s.T().Log("RemovePublisherIntent submitted successfully") + + s.T().Log("Verifying PublisherIntent correctly removed") + publisherIntentsResponse, err = pubsubQueryClient.QueryPublisherIntents(context.Background(), &types.QueryPublisherIntentsRequest{}) + s.Require().NoError(err) + s.Require().Len(publisherIntentsResponse.PublisherIntents, 0) + + // remove publisher prop + s.T().Log("Creating RemovePublisherProposal") + removePublisherProp := types.RemovePublisherProposal{ + Title: "remove a publisher", + Description: "example publisher is being removed", + Domain: "example.com", + } + + removePublisherPropMsg, err := govtypesv1beta1.NewMsgSubmitProposal( + &removePublisherProp, + sdk.Coins{ + { + Denom: testDenom, + Amount: math.NewInt(2), + }, + }, + proposer.address(), + ) + s.Require().NoError(err, "Unable to create governance proposal") + + s.submitAndVoteForProposal(proposerCtx, orch0ClientCtx, propID, removePublisherPropMsg) + propID++ + + s.T().Log("Verifying Publisher correctly removed") + publishersResponse, err = pubsubQueryClient.QueryPublishers(context.Background(), &types.QueryPublishersRequest{}) + s.Require().NoError(err) + s.Require().Len(publishersResponse.Publishers, 0) + + //////////////////////////////////////////////// + // Deleting a subscriber deletes child values // + //////////////////////////////////////////////// + + // for the purposes of the cascading deletion test, we recreate the state we had earlier in the test + + // add the publisher + s.T().Log("Creating AddPublisherProposal") + s.Require().NoError(err, "Unable to create governance proposal") + s.submitAndVoteForProposal(proposerCtx, orch0ClientCtx, propID, addPublisherPropMsg) + propID++ + + s.T().Log("Verifying Publisher correctly added") + publishersResponse, err = pubsubQueryClient.QueryPublishers(context.Background(), &types.QueryPublishersRequest{}) + s.Require().NoError(err) + s.Require().Len(publishersResponse.Publishers, 1) + publisher = publishersResponse.Publishers[0] + s.Require().Equal(publisher.Address, proposer.address().String()) + s.Require().Equal(publisher.CaCert, PublisherCACert) + s.Require().Equal(publisher.Domain, "example.com") + + // set publisher intent for cellar + _, err = s.chain.sendMsgs(*proposerCtx, &addPublisherIntentMsg) + s.Require().NoError(err) + s.T().Log("AddPublisherIntent submitted successfully") + + s.T().Log("Verifying PublisherIntent correctly added") + publisherIntentsResponse, err = pubsubQueryClient.QueryPublisherIntents(context.Background(), &types.QueryPublisherIntentsRequest{}) + s.Require().NoError(err) + s.Require().Len(publisherIntentsResponse.PublisherIntents, 1) + publisherIntent = publisherIntentsResponse.PublisherIntents[0] + s.Require().Equal(publisherIntent.SubscriptionId, subscriptionID) + s.Require().Equal(publisherIntent.PublisherDomain, publisher.Domain) + s.Require().Equal(publisherIntent.Method, types.PublishMethod_PUSH) + s.Require().Equal(publisherIntent.AllowedSubscribers, types.AllowedSubscribers_VALIDATORS) + + // add default subscription prop + s.T().Log("Creating AddDefaultSubscriptionProposal") + s.Require().NoError(err, "Unable to create governance proposal") + + s.submitAndVoteForProposal(proposerCtx, orch0ClientCtx, propID, addDefaultSubscriptionPropMsg) + + s.T().Log("Verifying DefaultSubscription correctly added") + defaultSubscriptionsResponse, err = pubsubQueryClient.QueryDefaultSubscriptions(context.Background(), &types.QueryDefaultSubscriptionsRequest{}) + s.Require().NoError(err) + s.Require().Len(defaultSubscriptionsResponse.DefaultSubscriptions, 1) + defaultSubscription = defaultSubscriptionsResponse.DefaultSubscriptions[0] + s.Require().Equal(defaultSubscription.SubscriptionId, subscriptionID) + s.Require().Equal(defaultSubscription.PublisherDomain, publisher.Domain) + + // create subscribers + s.T().Log("Creating Subscriber for two orchestrators") + + _, err = s.chain.sendMsgs(*orch0ClientCtx, &addSubscriber0Msg) + s.Require().NoError(err) + s.T().Log("AddSubscriber for orch 0 submitted correctly") + + _, err = s.chain.sendMsgs(*orch1ClientCtx, &addSubscriber1Msg) + s.Require().NoError(err) + s.T().Log("AddSubscriber for orch 1 submitted correctly") + + s.T().Log("Verifying Subscribers added correctly") + subscribersResponse, err = pubsubQueryClient.QuerySubscribers(context.Background(), &types.QuerySubscribersRequest{}) + s.Require().NoError(err) + s.Require().Len(subscribersResponse.Subscribers, 2) + + subscriber0 = subscribersResponse.Subscribers[0] + subscriber1 = subscribersResponse.Subscribers[1] + s.Require().Equal(subscriber0.Address, orch0.address().String()) + s.Require().Equal(subscriber0.CaCert, SubscriberCACert) + s.Require().Equal(subscriber0.PushUrl, subscriber0PushURL) + s.Require().Equal(subscriber1.Address, orch1.address().String()) + s.Require().Equal(subscriber1.CaCert, SubscriberCACert) + s.Require().Equal(subscriber1.PushUrl, subscriber1PushURL) + + // subscribe to the cellar + s.T().Log("Creating SubscriberIntent for both orchestrators") + + _, err = s.chain.sendMsgs(*orch0ClientCtx, &addSubscriberIntent0Msg) + s.Require().NoError(err) + s.T().Log("AddSubscriberIntent for orch 0 submitted correctly") + + _, err = s.chain.sendMsgs(*orch1ClientCtx, &addSubscriberIntent1Msg) + s.Require().NoError(err) + s.T().Log("AddSubscriberIntent for orch 1 submitted correctly") + + s.T().Log("Verifying SubscriberIntents added correctly") + subscriberIntentsResponse, err = pubsubQueryClient.QuerySubscriberIntents(context.Background(), &types.QuerySubscriberIntentsRequest{}) + s.Require().NoError(err) + s.Require().Len(subscriberIntentsResponse.SubscriberIntents, 2) + + subscriberIntent0 = subscriberIntentsResponse.SubscriberIntents[0] + subscriberIntent1 = subscriberIntentsResponse.SubscriberIntents[1] + s.Require().Equal(subscriberIntent0.SubscriptionId, subscriptionID) + s.Require().Equal(subscriberIntent0.SubscriberAddress, orch0.address().String()) + s.Require().Equal(subscriberIntent0.PublisherDomain, publisher.Domain) + s.Require().Equal(subscriberIntent1.SubscriptionId, subscriptionID) + s.Require().Equal(subscriberIntent1.SubscriberAddress, orch1.address().String()) + s.Require().Equal(subscriberIntent1.PublisherDomain, publisher.Domain) + + // now we delete subscriber 0, which should also delete their publisher intents + s.T().Log("Removing Subscriber for orch 0") + + _, err = s.chain.sendMsgs(*orch0ClientCtx, &removeSubscriber0Msg) + s.Require().NoError(err) + s.T().Log("RemoveSubscriber for orch 0 submitted correctly") + + s.T().Log("Verifying Subscriber for orch 0 removed") + subscribersResponse, err = pubsubQueryClient.QuerySubscribers(context.Background(), &types.QuerySubscribersRequest{}) + s.Require().NoError(err) + s.Require().Len(subscribersResponse.Subscribers, 1) + s.Require().Equal(subscribersResponse.Subscribers[0].Address, orch1.address().String()) + + // now check that there are no subscriber intents for the deleted subscriber 0, but there is still one for subscriber 1 + subscriberIntentsByAddr0Response, err := pubsubQueryClient.QuerySubscriberIntentsBySubscriberAddress(context.Background(), + &types.QuerySubscriberIntentsBySubscriberAddressRequest{SubscriberAddress: orch0.address().String()}) + s.Require().NoError(err) + s.Require().Len(subscriberIntentsByAddr0Response.SubscriberIntents, 0) + subscriberIntentsByAddr1Response, err := pubsubQueryClient.QuerySubscriberIntentsBySubscriberAddress(context.Background(), + &types.QuerySubscriberIntentsBySubscriberAddressRequest{SubscriberAddress: orch1.address().String()}) + s.Require().NoError(err) + s.Require().Len(subscriberIntentsByAddr1Response.SubscriberIntents, 1) + + /////////////////////////////////////////////// + // Deleting a publisher deletes child values // + /////////////////////////////////////////////// + + // now we are going to delete this publisher, which will remove both its default subscriptions and its publisher intents + // as a side effect of deleting a publisher intent, it should delete the attached subscriber intents + // we will also use the self-deletion message here rather than a gov prop + + s.T().Log("Creating RemovePublisher message") + removePublisherMsg := types.MsgRemovePublisherRequest{ + PublisherDomain: publisher.Domain, + Signer: proposer.address().String(), + } + + _, err = s.chain.sendMsgs(*proposerCtx, &removePublisherMsg) + s.Require().NoError(err) + s.T().Log("RemovePublisher submitted correctly") + + s.T().Log("Verifying all the child values have been removed with the publisher") + // now everything should be gone, the only thing that should be left is one subscriber value + publishersResponse, err = pubsubQueryClient.QueryPublishers(context.Background(), &types.QueryPublishersRequest{}) + s.Require().NoError(err) + s.Require().Len(publishersResponse.Publishers, 0) + defaultSubscriptionsResponse, err = pubsubQueryClient.QueryDefaultSubscriptions(context.Background(), &types.QueryDefaultSubscriptionsRequest{}) + s.Require().NoError(err) + s.Require().Len(defaultSubscriptionsResponse.DefaultSubscriptions, 0) + publisherIntentsResponse, err = pubsubQueryClient.QueryPublisherIntents(context.Background(), &types.QueryPublisherIntentsRequest{}) + s.Require().NoError(err) + s.Require().Len(publisherIntentsResponse.PublisherIntents, 0) + subscriberIntentsResponse, err = pubsubQueryClient.QuerySubscriberIntents(context.Background(), &types.QuerySubscriberIntentsRequest{}) + s.Require().NoError(err) + s.Require().Len(subscriberIntentsResponse.SubscriberIntents, 0) + subscribersResponse, err = pubsubQueryClient.QuerySubscribers(context.Background(), &types.QuerySubscribersRequest{}) + s.Require().NoError(err) + s.Require().Len(subscribersResponse.Subscribers, 1) + s.Require().Equal(subscribersResponse.Subscribers[0].Address, orch1.address().String()) + }) +} + +func (s *IntegrationTestSuite) submitAndVoteForProposal(proposerCtx *client.Context, orchClientCtx *client.Context, propID uint64, proposalMsg *govtypesv1beta1.MsgSubmitProposal) { + s.T().Log("Submit proposal") + submitProposalResponse, err := s.chain.sendMsgs(*proposerCtx, proposalMsg) + s.Require().NoError(err) + s.Require().Zero(submitProposalResponse.Code, "raw log: %s", submitProposalResponse.RawLog) + + s.T().Log("Check proposal was submitted correctly") + govQueryClient := govtypesv1beta1.NewQueryClient(orchClientCtx) + + s.Require().Eventually(func() bool { + proposalsQueryResponse, err := govQueryClient.Proposals(context.Background(), &govtypesv1beta1.QueryProposalsRequest{}) + if err != nil { + s.T().Logf("error querying proposals: %e", err) + return false + } + + s.Require().NotEmpty(proposalsQueryResponse.Proposals) + s.Require().Equal(propID, proposalsQueryResponse.Proposals[propID-1].ProposalId, "not proposal id %d", propID) + s.Require().Equal(govtypesv1beta1.StatusVotingPeriod, proposalsQueryResponse.Proposals[propID-1].Status, "proposal not in voting period") + + return true + }, time.Second*30, time.Second*5, "proposal submission was never found") + + s.T().Log("Vote for proposal") + for _, val := range s.chain.validators { + kr, err := val.keyring() + s.Require().NoError(err) + localClientCtx, err := s.chain.clientContext("tcp://localhost:26657", &kr, "val", val.address()) + s.Require().NoError(err) + + voteMsg := govtypesv1beta1.NewMsgVote(val.address(), propID, govtypesv1beta1.OptionYes) + voteResponse, err := s.chain.sendMsgs(*localClientCtx, voteMsg) + s.Require().NoError(err) + s.Require().Zero(voteResponse.Code, "Vote error: %s", voteResponse.RawLog) + } + + s.T().Log("Waiting for proposal to be approved") + s.Require().Eventually(func() bool { + proposalQueryResponse, _ := govQueryClient.Proposal(context.Background(), &govtypesv1beta1.QueryProposalRequest{ProposalId: propID}) + return govtypesv1beta1.StatusPassed == proposalQueryResponse.Proposal.Status + }, time.Second*30, time.Second*5, "proposal was never accepted") + s.T().Log("Proposal approved!") +} + +const PublisherCACert = `-----BEGIN CERTIFICATE----- +MIICGzCCAaKgAwIBAgIUVYhZ4+pC7vQAf5FC6pssLk/eq5YwCgYIKoZIzj0EAwMw +RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu +dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjAxMDUwNzIwMzFaFw0yNDAxMDUw +NzIwMzFaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD +VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQ3jwZd0Xe9w55UyAxRuc4F2u/LDdo7ykCZBO34neXpLR4GRRpx5VjFdHcX +WjvM9j3DnWjptb1fe7TIKSSJRmW1skWkpktOthIPhfga9jBhU4WRUDloKk1tRuiI +e8rRSlSjUzBRMB0GA1UdDgQWBBSTyTULHT9hNAA2Wg4dCtuTuIhiXTAfBgNVHSME +GDAWgBSTyTULHT9hNAA2Wg4dCtuTuIhiXTAPBgNVHRMBAf8EBTADAQH/MAoGCCqG +SM49BAMDA2cAMGQCMEd+Eg6lhStLkWEwmJJGN3Xdh9JmNsgsdff3mI3Y7UmHOB8K +HOqHGS8ApZcunRauDAIwRtgceZpkS92KuP3QOUotAH/nnCzp7X1lVzGOSTBRTVYJ +pohf4PJrfacqpi7PoXBk +-----END CERTIFICATE----- +` + +const SubscriberCACert = `-----BEGIN CERTIFICATE----- +MIICHTCCAaKgAwIBAgIUTYD5x0zSg1rOztoJK8OEgWDl+yYwCgYIKoZIzj0EAwMw +RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu +dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjAxMDUwNzIwMjlaFw0yNDAxMDUw +NzIwMjlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD +VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAATi4OkAJaqyWwS1F6mBCBftwF/K02Zl07pg2C/WJxZEaGI/cRVTELt4Qsy2 +7SiGcJLIIsTQXfdNkyRue20/J/SpUDPMVbWNCozC2bS4DWd1n9uHlSMT4h7gZqxf +SkkkecCjUzBRMB0GA1UdDgQWBBSngShmDy8kt2azMqFGD1ObYaXT0DAfBgNVHSME +GDAWgBSngShmDy8kt2azMqFGD1ObYaXT0DAPBgNVHRMBAf8EBTADAQH/MAoGCCqG +SM49BAMDA2kAMGYCMQCel/W4B/LB75j0WHEHrKSoED17D4w+OrXlK6wnpVRSyOmZ +A0B4pBO4uh3ldwCZnBACMQC0whN1TI8a9Ku90nfvZ+D2kKMg/p39SmCDadQJNzwc +kp4YI2VJp0zYzt/xLiBRbZc= +-----END CERTIFICATE----- +` diff --git a/integration_tests/scheduled_cork_test.go b/integration_tests/scheduled_cork_test.go index 51f94677..ed2d2fb1 100644 --- a/integration_tests/scheduled_cork_test.go +++ b/integration_tests/scheduled_cork_test.go @@ -3,11 +3,18 @@ package integration_tests import ( "context" "encoding/hex" + "fmt" + "strings" "time" + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" govtypesv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" gbtypes "github.com/peggyjv/gravity-bridge/module/v4/x/gravity/types" "github.com/peggyjv/sommelier/v7/x/cork/types" ) @@ -309,3 +316,97 @@ func (s *IntegrationTestSuite) TestScheduledCork() { s.T().Log("Proposal approved!") }) } + +const CounterABI = ` + [ + { + "inputs": [], + "name": "count", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "dec", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "get", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "inc", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ] +` + +func ABIEncodedGet() []byte { + encodedCall, err := abi.JSON(strings.NewReader(CounterABI)) + if err != nil { + panic(errorsmod.Wrap(err, "bad ABI definition in code")) + } + + abiEncodedCall, err := encodedCall.Pack("get") + if err != nil { + panic(err) + } + + return abiEncodedCall +} + +func ABIEncodedInc() []byte { + encodedCall, err := abi.JSON(strings.NewReader(CounterABI)) + if err != nil { + panic(errorsmod.Wrap(err, "bad ABI definition in code")) + } + + abiEncodedCall, err := encodedCall.Pack("inc") + if err != nil { + panic(err) + } + + return abiEncodedCall +} + +func (s *IntegrationTestSuite) getCurrentCount() (*math.Int, error) { + ethClient, err := ethclient.Dial(fmt.Sprintf("http://%s", s.ethResource.GetHostPort("8545/tcp"))) + if err != nil { + return nil, err + } + + bz, err := ethClient.CallContract(context.Background(), ethereum.CallMsg{ + From: common.HexToAddress(s.chain.validators[0].ethereumKey.address), + To: &counterContract, + Gas: 0, + Data: ABIEncodedGet(), + }, nil) + if err != nil { + return nil, err + } + + count := UnpackEthUInt(bz) + + return &count, nil +} diff --git a/integration_tests/tls/client/test_client.crt b/integration_tests/tls/client/test_client.crt new file mode 100644 index 00000000..7c6d56dc --- /dev/null +++ b/integration_tests/tls/client/test_client.crt @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICTjCCAdOgAwIBAgIUHYInHlXtDU99l6ZCL94z+Y6wDfEwCgYIKoZIzj0EAwMw +RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu +dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjAxMDUwNzIwNDVaFw0yNDAxMDUw +NzIwNDVaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD +VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9z +dDB2MBAGByqGSM49AgEGBSuBBAAiA2IABBMeRNpHRztZEFwbUg4XfZwaF+qHFGwU +GqX2FFZP7kqbhXXsVZEvTyTCR5BP7rlaGbNlClrqfxTfHevnFN7b7p7wj34ieIc8 +oVwJnUarewP7n+FcpW+VFr0VI6MLLE8rw6NwMG4wHQYDVR0OBBYEFM+2bMCT7tOF +c3tuyRPwB3dFfYIwMB8GA1UdIwQYMBaAFJPJNQsdP2E0ADZaDh0K25O4iGJdMAkG +A1UdEwQCMAAwFAYDVR0RBA0wC4IJbG9jYWxob3N0MAsGA1UdDwQEAwIE8DAKBggq +hkjOPQQDAwNpADBmAjEAjf8WRKs9hPKLLBKOWkUzYvzt98tgYSrvFD4EWskouRS4 +/FU0psQcFj3zw/M78OdJAjEA8Zq5iP2MSU21vgJFFq8dfKCh8joQShR5eQ6TF0sC +y6/np5w9/suxM2F25xk3FRDV +-----END CERTIFICATE----- diff --git a/integration_tests/tls/client/test_client_ca.crt b/integration_tests/tls/client/test_client_ca.crt new file mode 100644 index 00000000..7a7178ab --- /dev/null +++ b/integration_tests/tls/client/test_client_ca.crt @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICGzCCAaKgAwIBAgIUVYhZ4+pC7vQAf5FC6pssLk/eq5YwCgYIKoZIzj0EAwMw +RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu +dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjAxMDUwNzIwMzFaFw0yNDAxMDUw +NzIwMzFaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD +VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQ3jwZd0Xe9w55UyAxRuc4F2u/LDdo7ykCZBO34neXpLR4GRRpx5VjFdHcX +WjvM9j3DnWjptb1fe7TIKSSJRmW1skWkpktOthIPhfga9jBhU4WRUDloKk1tRuiI +e8rRSlSjUzBRMB0GA1UdDgQWBBSTyTULHT9hNAA2Wg4dCtuTuIhiXTAfBgNVHSME +GDAWgBSTyTULHT9hNAA2Wg4dCtuTuIhiXTAPBgNVHRMBAf8EBTADAQH/MAoGCCqG +SM49BAMDA2cAMGQCMEd+Eg6lhStLkWEwmJJGN3Xdh9JmNsgsdff3mI3Y7UmHOB8K +HOqHGS8ApZcunRauDAIwRtgceZpkS92KuP3QOUotAH/nnCzp7X1lVzGOSTBRTVYJ +pohf4PJrfacqpi7PoXBk +-----END CERTIFICATE----- diff --git a/integration_tests/tls/client/test_client_ca.srl b/integration_tests/tls/client/test_client_ca.srl new file mode 100644 index 00000000..998d53d3 --- /dev/null +++ b/integration_tests/tls/client/test_client_ca.srl @@ -0,0 +1 @@ +1D82271E55ED0D4F7D97A6422FDE33F98EB00DF1 diff --git a/integration_tests/tls/client/test_client_ca_key_non-pkcs8.pem b/integration_tests/tls/client/test_client_ca_key_non-pkcs8.pem new file mode 100644 index 00000000..8224abfc --- /dev/null +++ b/integration_tests/tls/client/test_client_ca_key_non-pkcs8.pem @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDDSNJ9TULBnhwiLGJwCtiG+R62qg65/g2XpO3Dl6o3ePuyrLVRPRZuY +klZszxKPfNSgBwYFK4EEACKhZANiAAQ3jwZd0Xe9w55UyAxRuc4F2u/LDdo7ykCZ +BO34neXpLR4GRRpx5VjFdHcXWjvM9j3DnWjptb1fe7TIKSSJRmW1skWkpktOthIP +hfga9jBhU4WRUDloKk1tRuiIe8rRSlQ= +-----END EC PRIVATE KEY----- diff --git a/integration_tests/tls/client/test_client_ca_key_pkcs8.pem b/integration_tests/tls/client/test_client_ca_key_pkcs8.pem new file mode 100644 index 00000000..20fafb73 --- /dev/null +++ b/integration_tests/tls/client/test_client_ca_key_pkcs8.pem @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDSNJ9TULBnhwiLGJwC +tiG+R62qg65/g2XpO3Dl6o3ePuyrLVRPRZuYklZszxKPfNShZANiAAQ3jwZd0Xe9 +w55UyAxRuc4F2u/LDdo7ykCZBO34neXpLR4GRRpx5VjFdHcXWjvM9j3DnWjptb1f +e7TIKSSJRmW1skWkpktOthIPhfga9jBhU4WRUDloKk1tRuiIe8rRSlQ= +-----END PRIVATE KEY----- diff --git a/integration_tests/tls/client/test_client_key_non-pkcs8.pem b/integration_tests/tls/client/test_client_key_non-pkcs8.pem new file mode 100644 index 00000000..b8f0d195 --- /dev/null +++ b/integration_tests/tls/client/test_client_key_non-pkcs8.pem @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDCHaVrGiqE73xC+nuzTz4VJy4N3zvnDR4VC/q25lDby/pcUG4wSOHVy +SXHAaO4nMD2gBwYFK4EEACKhZANiAAQTHkTaR0c7WRBcG1IOF32cGhfqhxRsFBql +9hRWT+5Km4V17FWRL08kwkeQT+65WhmzZQpa6n8U3x3r5xTe2+6e8I9+IniHPKFc +CZ1Gq3sD+5/hXKVvlRa9FSOjCyxPK8M= +-----END EC PRIVATE KEY----- diff --git a/integration_tests/tls/client/test_client_key_pkcs8.pem b/integration_tests/tls/client/test_client_key_pkcs8.pem new file mode 100644 index 00000000..64286e38 --- /dev/null +++ b/integration_tests/tls/client/test_client_key_pkcs8.pem @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCHaVrGiqE73xC+nuzT +z4VJy4N3zvnDR4VC/q25lDby/pcUG4wSOHVySXHAaO4nMD2hZANiAAQTHkTaR0c7 +WRBcG1IOF32cGhfqhxRsFBql9hRWT+5Km4V17FWRL08kwkeQT+65WhmzZQpa6n8U +3x3r5xTe2+6e8I9+IniHPKFcCZ1Gq3sD+5/hXKVvlRa9FSOjCyxPK8M= +-----END PRIVATE KEY----- diff --git a/integration_tests/tls/regenerate_keys_and_certs.sh b/integration_tests/tls/regenerate_keys_and_certs.sh new file mode 100755 index 00000000..567871b8 --- /dev/null +++ b/integration_tests/tls/regenerate_keys_and_certs.sh @@ -0,0 +1,89 @@ +#!/bin/sh + +set -ex + +# These commands are correct for generating certificates using openssl 1.1.1l +# Check your version by running `openssl version` and if it's LibreSSL, install +# the proper version of openssl using a tool like homebrew + +# Set up folders if they don't exist yet + +mkdir -p server +mkdir -p client + +# Create the private keys + +openssl ecparam -name secp384r1 -noout -out server/test_server_ca_key_non-pkcs8.pem -genkey +openssl ecparam -name secp384r1 -noout -out server/test_server_key_non-pkcs8.pem -genkey +openssl ecparam -name secp384r1 -noout -out client/test_client_ca_key_non-pkcs8.pem -genkey +openssl ecparam -name secp384r1 -noout -out client/test_client_key_non-pkcs8.pem -genkey + +# Create PKCS8 versions of the private keys, which the Rust libraries expect + +openssl pkcs8 -in server/test_server_ca_key_non-pkcs8.pem -out server/test_server_ca_key_pkcs8.pem -topk8 -nocrypt +openssl pkcs8 -in server/test_server_key_non-pkcs8.pem -out server/test_server_key_pkcs8.pem -topk8 -nocrypt +openssl pkcs8 -in client/test_client_ca_key_non-pkcs8.pem -out client/test_client_ca_key_pkcs8.pem -topk8 -nocrypt +openssl pkcs8 -in client/test_client_key_non-pkcs8.pem -out client/test_client_key_pkcs8.pem -topk8 -nocrypt + +# Create CAs + +set +x +echo +echo "You're going to be asked to fill in fields for the server and client CAs." +echo "These values don't really matter, just go with the defaults." +read -p "Press enter to continue." +echo +echo "=====================" +echo "Server CA Certificate" +echo "=====================" +echo +set -x + +openssl req -x509 -new -key server/test_server_ca_key_pkcs8.pem -out server/test_server_ca.crt -sha384 -days 730 + +set +x +echo +echo "=====================" +echo "Client CA Certificate" +echo "=====================" +echo +set -x + +openssl req -x509 -new -key client/test_client_ca_key_pkcs8.pem -out client/test_client_ca.crt -sha384 -days 730 + +# Create CSRs + +set +x +echo +echo "You're going to be asked to fill in fields first for the server and client certificates." +echo "The only important setting is to make sure the Common Name is set to localhost" +read -p "Press enter to continue." +echo +echo "==================" +echo "Server Certificate" +echo "==================" +echo +set -x + +openssl req -new -sha384 -key server/test_server_key_pkcs8.pem -out server/test_server.csr + +set +x +echo +echo "==================" +echo "Client Certificate" +echo "==================" +echo +set -x + +openssl req -new -sha384 -key client/test_client_key_pkcs8.pem -out client/test_client.csr + +# Sign the server and client certificates with the respective CAs +# the v3.ext file makes sure we include the subjectAltName=localhost extension + +openssl x509 -req -in server/test_server.csr -CA server/test_server_ca.crt -CAkey server/test_server_ca_key_pkcs8.pem -CAcreateserial -out server/test_server.crt -sha384 -extfile v3.ext -days 730 +openssl x509 -req -in client/test_client.csr -CA client/test_client_ca.crt -CAkey client/test_client_ca_key_pkcs8.pem -CAcreateserial -out client/test_client.crt -sha384 -extfile v3.ext -days 730 + +# Clean up the certificate requests + +rm server/test_server.csr || true +rm client/test_client.csr || true diff --git a/integration_tests/tls/server/test_server.crt b/integration_tests/tls/server/test_server.crt new file mode 100644 index 00000000..8336106d --- /dev/null +++ b/integration_tests/tls/server/test_server.crt @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICTDCCAdOgAwIBAgIULiHn5Yo9yHfRhJz7c7mOjTB9rOUwCgYIKoZIzj0EAwMw +RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu +dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjAxMDUwNzIwNDVaFw0yNDAxMDUw +NzIwNDVaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD +VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9z +dDB2MBAGByqGSM49AgEGBSuBBAAiA2IABBdm6RPp1VOTzPPijPAtvNKhLjEttM1Q +YcpKXgSr/VkadYBlHvIBBJmJJg30gyXMydEWrU1e39immN+n6DVsK0iUvb0v2+oz +dhkmFp4tDRFPZxUkImRNju8EY/+6fJTSqaNwMG4wHQYDVR0OBBYEFEXqknmA1wHN +gHf7utuKGGoTLbGBMB8GA1UdIwQYMBaAFKeBKGYPLyS3ZrMyoUYPU5thpdPQMAkG +A1UdEwQCMAAwFAYDVR0RBA0wC4IJbG9jYWxob3N0MAsGA1UdDwQEAwIE8DAKBggq +hkjOPQQDAwNnADBkAjBv/a88BUEDmy8rNCIsLTmqnJbiYGNEUfOY6XIKJdO0q6aY +Ox93p/F4IUeSdO24C4sCMDobRizWojplC72AvIb5JCuAg7RfeJUXoYIW2xQe/oTs +9CV+cydPvC8kTiMSBZTWjQ== +-----END CERTIFICATE----- diff --git a/integration_tests/tls/server/test_server_ca.crt b/integration_tests/tls/server/test_server_ca.crt new file mode 100644 index 00000000..50ee616f --- /dev/null +++ b/integration_tests/tls/server/test_server_ca.crt @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICHTCCAaKgAwIBAgIUTYD5x0zSg1rOztoJK8OEgWDl+yYwCgYIKoZIzj0EAwMw +RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu +dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjAxMDUwNzIwMjlaFw0yNDAxMDUw +NzIwMjlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD +VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAATi4OkAJaqyWwS1F6mBCBftwF/K02Zl07pg2C/WJxZEaGI/cRVTELt4Qsy2 +7SiGcJLIIsTQXfdNkyRue20/J/SpUDPMVbWNCozC2bS4DWd1n9uHlSMT4h7gZqxf +SkkkecCjUzBRMB0GA1UdDgQWBBSngShmDy8kt2azMqFGD1ObYaXT0DAfBgNVHSME +GDAWgBSngShmDy8kt2azMqFGD1ObYaXT0DAPBgNVHRMBAf8EBTADAQH/MAoGCCqG +SM49BAMDA2kAMGYCMQCel/W4B/LB75j0WHEHrKSoED17D4w+OrXlK6wnpVRSyOmZ +A0B4pBO4uh3ldwCZnBACMQC0whN1TI8a9Ku90nfvZ+D2kKMg/p39SmCDadQJNzwc +kp4YI2VJp0zYzt/xLiBRbZc= +-----END CERTIFICATE----- diff --git a/integration_tests/tls/server/test_server_ca.srl b/integration_tests/tls/server/test_server_ca.srl new file mode 100644 index 00000000..b16eea03 --- /dev/null +++ b/integration_tests/tls/server/test_server_ca.srl @@ -0,0 +1 @@ +2E21E7E58A3DC877D1849CFB73B98E8D307DACE5 diff --git a/integration_tests/tls/server/test_server_ca_key_non-pkcs8.pem b/integration_tests/tls/server/test_server_ca_key_non-pkcs8.pem new file mode 100644 index 00000000..b21c545c --- /dev/null +++ b/integration_tests/tls/server/test_server_ca_key_non-pkcs8.pem @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDCINLgOt9An518Pe/sciHH5GoBAlJ4iwljb5Ai1II1H+8/mwSeTPiIU +nzvrJGlS4mqgBwYFK4EEACKhZANiAATi4OkAJaqyWwS1F6mBCBftwF/K02Zl07pg +2C/WJxZEaGI/cRVTELt4Qsy27SiGcJLIIsTQXfdNkyRue20/J/SpUDPMVbWNCozC +2bS4DWd1n9uHlSMT4h7gZqxfSkkkecA= +-----END EC PRIVATE KEY----- diff --git a/integration_tests/tls/server/test_server_ca_key_pkcs8.pem b/integration_tests/tls/server/test_server_ca_key_pkcs8.pem new file mode 100644 index 00000000..14f4c920 --- /dev/null +++ b/integration_tests/tls/server/test_server_ca_key_pkcs8.pem @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCINLgOt9An518Pe/sc +iHH5GoBAlJ4iwljb5Ai1II1H+8/mwSeTPiIUnzvrJGlS4mqhZANiAATi4OkAJaqy +WwS1F6mBCBftwF/K02Zl07pg2C/WJxZEaGI/cRVTELt4Qsy27SiGcJLIIsTQXfdN +kyRue20/J/SpUDPMVbWNCozC2bS4DWd1n9uHlSMT4h7gZqxfSkkkecA= +-----END PRIVATE KEY----- diff --git a/integration_tests/tls/server/test_server_key_non-pkcs8.pem b/integration_tests/tls/server/test_server_key_non-pkcs8.pem new file mode 100644 index 00000000..4fe4d4fc --- /dev/null +++ b/integration_tests/tls/server/test_server_key_non-pkcs8.pem @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDBJbcF9c2ijMxx2nc6naCJgVHfaqzmcGaDMyi5ua/o817xpAgf19YUb +ajTxc7zv5qSgBwYFK4EEACKhZANiAAQXZukT6dVTk8zz4ozwLbzSoS4xLbTNUGHK +Sl4Eq/1ZGnWAZR7yAQSZiSYN9IMlzMnRFq1NXt/Yppjfp+g1bCtIlL29L9vqM3YZ +JhaeLQ0RT2cVJCJkTY7vBGP/unyU0qk= +-----END EC PRIVATE KEY----- diff --git a/integration_tests/tls/server/test_server_key_pkcs8.pem b/integration_tests/tls/server/test_server_key_pkcs8.pem new file mode 100644 index 00000000..23dd049d --- /dev/null +++ b/integration_tests/tls/server/test_server_key_pkcs8.pem @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBJbcF9c2ijMxx2nc6n +aCJgVHfaqzmcGaDMyi5ua/o817xpAgf19YUbajTxc7zv5qShZANiAAQXZukT6dVT +k8zz4ozwLbzSoS4xLbTNUGHKSl4Eq/1ZGnWAZR7yAQSZiSYN9IMlzMnRFq1NXt/Y +ppjfp+g1bCtIlL29L9vqM3YZJhaeLQ0RT2cVJCJkTY7vBGP/unyU0qk= +-----END PRIVATE KEY----- diff --git a/integration_tests/tls/v3.ext b/integration_tests/tls/v3.ext new file mode 100644 index 00000000..b55ebdee --- /dev/null +++ b/integration_tests/tls/v3.ext @@ -0,0 +1,5 @@ +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer +basicConstraints=CA:FALSE +subjectAltName=DNS:localhost +keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment diff --git a/x/pubsub/client/cli/tx.go b/x/pubsub/client/cli/tx.go index 39d91f4d..e099c4ca 100644 --- a/x/pubsub/client/cli/tx.go +++ b/x/pubsub/client/cli/tx.go @@ -327,8 +327,8 @@ func GetCmdAddPublisherPullIntent() *cobra.Command { Short: "Add a publisher intent with a pull URL", Long: strings.TrimSpace( fmt.Sprintf(`Examples: -$ %s tx pubsub add-publisher-pull-intent 0x123801a7D398351b8bE11C439e05C5B3259aeC9B sommelier.example.com "https://sommelier.example.com/pull" VALIDATORS --from= -$ %s tx pubsub add-publisher-pull-intent 0x123801a7D398351b8bE11C439e05C5B3259aeC9B sommelier.example.com "https://sommelier.example.com/pull" LIST somm1y6d5kasehecexf09ka6y0ggl0pxzt6dg6n8lw0,somm18ld4633yswcyjdklej3att6aw93nhlf7596qkk --from= +$ %s tx pubsub add-publisher-pull-intent 1:0x123801a7D398351b8bE11C439e05C5B3259aeC9B sommelier.example.com "https://sommelier.example.com/pull" VALIDATORS --from= +$ %s tx pubsub add-publisher-pull-intent 1:0x123801a7D398351b8bE11C439e05C5B3259aeC9B sommelier.example.com "https://sommelier.example.com/pull" LIST somm1y6d5kasehecexf09ka6y0ggl0pxzt6dg6n8lw0,somm18ld4633yswcyjdklej3att6aw93nhlf7596qkk --from= `, version.AppName, version.AppName), ), RunE: func(cmd *cobra.Command, args []string) error { @@ -385,8 +385,8 @@ func GetCmdAddPublisherPushIntent() *cobra.Command { Short: "Add a publisher intent that will push", Long: strings.TrimSpace( fmt.Sprintf(`Examples: -$ %s tx pubsub add-publisher-push-intent 0x123801a7D398351b8bE11C439e05C5B3259aeC9B sommelier.example.com VALIDATORS --from= -$ %s tx pubsub add-publisher-push-intent 0x123801a7D398351b8bE11C439e05C5B3259aeC9B sommelier.example.com LIST somm1y6d5kasehecexf09ka6y0ggl0pxzt6dg6n8lw0,somm18ld4633yswcyjdklej3att6aw93nhlf7596qkk --from= +$ %s tx pubsub add-publisher-push-intent 1:0x123801a7D398351b8bE11C439e05C5B3259aeC9B sommelier.example.com VALIDATORS --from= +$ %s tx pubsub add-publisher-push-intent 1:0x123801a7D398351b8bE11C439e05C5B3259aeC9B sommelier.example.com LIST somm1y6d5kasehecexf09ka6y0ggl0pxzt6dg6n8lw0,somm18ld4633yswcyjdklej3att6aw93nhlf7596qkk --from= `, version.AppName, version.AppName), ), RunE: func(cmd *cobra.Command, args []string) error { @@ -443,7 +443,7 @@ func GetCmdAddSubscriberIntent() *cobra.Command { Short: "Add a subscriber intent", Long: strings.TrimSpace( fmt.Sprintf(`Examples: -$ %s tx pubsub add-subscriber-intent 0x123801a7D398351b8bE11C439e05C5B3259aeC9B somm1y6d5kasehecexf09ka6y0ggl0pxzt6dg6n8lw0 pullpublisher.example.com --from= +$ %s tx pubsub add-subscriber-intent 1:0x123801a7D398351b8bE11C439e05C5B3259aeC9B somm1y6d5kasehecexf09ka6y0ggl0pxzt6dg6n8lw0 pullpublisher.example.com --from= `, version.AppName), ), RunE: func(cmd *cobra.Command, args []string) error { @@ -544,7 +544,7 @@ func GetCmdRemovePublisherIntent() *cobra.Command { Short: "Remove a publisher intent", Long: strings.TrimSpace( fmt.Sprintf(`Examples: -$ %s tx pubsub remove-publisher-intent 0x123801a7D398351b8bE11C439e05C5B3259aeC9B pushpublisher.example.com --from= +$ %s tx pubsub remove-publisher-intent 1:0x123801a7D398351b8bE11C439e05C5B3259aeC9B pushpublisher.example.com --from= `, version.AppName), ), RunE: func(cmd *cobra.Command, args []string) error { @@ -578,7 +578,7 @@ func GetCmdRemoveSubscriberIntent() *cobra.Command { Short: "Remove a subcriber intent", Long: strings.TrimSpace( fmt.Sprintf(`Examples: -$ %s tx pubsub remove-subscriber-intent 0x123801a7D398351b8bE11C439e05C5B3259aeC9B somm1y6d5kasehecexf09ka6y0ggl0pxzt6dg6n8lw0 --from= +$ %s tx pubsub remove-subscriber-intent 1:0x123801a7D398351b8bE11C439e05C5B3259aeC9B somm1y6d5kasehecexf09ka6y0ggl0pxzt6dg6n8lw0 --from= `, version.AppName), ), RunE: func(cmd *cobra.Command, args []string) error { diff --git a/x/pubsub/keeper/keeper.go b/x/pubsub/keeper/keeper.go index 910a8708..becf9739 100644 --- a/x/pubsub/keeper/keeper.go +++ b/x/pubsub/keeper/keeper.go @@ -109,6 +109,12 @@ func (k Keeper) DeletePublisher(ctx sdk.Context, publisherDomain string) { for _, publisherIntent := range k.GetPublisherIntentsByPublisherDomain(ctx, publisherDomain) { k.DeletePublisherIntent(ctx, publisherIntent.SubscriptionId, publisherIntent.PublisherDomain) } + + for _, defaultSubscription := range k.GetDefaultSubscriptions(ctx) { + if defaultSubscription.PublisherDomain == publisherDomain { + k.DeleteDefaultSubscription(ctx, defaultSubscription.SubscriptionId) + } + } } //////////////// diff --git a/x/pubsub/keeper/msg_server.go b/x/pubsub/keeper/msg_server.go index a79f288c..e10f5bc2 100644 --- a/x/pubsub/keeper/msg_server.go +++ b/x/pubsub/keeper/msg_server.go @@ -71,7 +71,7 @@ func (k Keeper) AddSubscriberIntent(c context.Context, msg *types.MsgAddSubscrib signer := msg.MustGetSigner() signerAddress := signer.String() - if signerAddress != subscriberIntent.SubscriberAddress { + if signerAddress != subscriberAddress { return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "registered subscriber address must be signer: %s", subscriberIntent.SubscriberAddress) } @@ -97,7 +97,7 @@ func (k Keeper) AddSubscriberIntent(c context.Context, msg *types.MsgAddSubscrib } if publisherIntent.AllowedSubscribers == types.AllowedSubscribers_VALIDATORS { - // TODO(bolten): this implementation ends up making the module sort of non-generic but is necessary to allow + // this implementation ends up making the module sort of non-generic but is necessary to allow // orchestrator keys to manipulate subscriptions var validatorI stakingtypes.ValidatorI if validator := k.gravityKeeper.GetOrchestratorValidatorAddress(ctx, signer); validator == nil {