Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subscription manager development #3

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
target
Cargo.lock
contract.wasm*
node_modules
subscription-manager/tests/package-lock.json

# Cargo+Git helper file (https://github.com/rust-lang/cargo/blob/0.44.1/src/cargo/sources/git/utils.rs#L320-L327)
.cargo-ok
Expand Down
11 changes: 11 additions & 0 deletions subscription-manager/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
contract-opt.wasm
Dockerfile

permit.json
signed_permit.json
claive_subscription_manager.wasm
permit_to_sign.json
query_subscriber_permit.json
api_keys_permit.json
api_keys_by_identity_permit.json
/optimized-wasm
23 changes: 17 additions & 6 deletions subscription-manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,27 @@ overflow-checks = true

[features]
default = []
# debug-print = ["cosmwasm-std/debug-print"] doesn't work anymore?
# for quicker tests, cargo test --lib
# for more explicit tests, cargo test --features=backtraces
backtraces = ["cosmwasm-std/backtraces"]
schema = ["cosmwasm-schema"]

[dependencies]
cosmwasm-std = { git = "https://github.com/scrtlabs/cosmwasm", tag = "v1.1.9-secret" }
cosmwasm-storage = { git = "https://github.com/scrtlabs/cosmwasm", tag = "v1.1.9-secret" }
schemars = { version = "0.8.11" }
serde = { version = "1.0" }
thiserror = { version = "1.0" }
cosmwasm-schema = "1.0.0"
cosmwasm-schema = { version = "1.1.0", optional = true }
cosmwasm-std = { package = "secret-cosmwasm-std", version = "1.1.11" , features = ["stargate", "random"]}
cosmwasm-storage = { package = "secret-cosmwasm-storage", version = "1.1.11" }
schemars = "0.8.11"
secret-toolkit = { version = "0.10.0", default-features = false, features = ["utils", "storage", "serialization", "viewing-key", "permit"] }
serde = { version = "1.0.144", default-features = false, features = ["derive"] }
serde-json-wasm = "1.0.0"
sha3 = "0.10.4"
base64 = "0.22.1"
anybuf = "0.5.0"
cc = { version = "=1.1.10" }
serde_json = "1.0.134"
hex = "0.4.3"
sha2 = "0.9.9"

# Uncomment these for some common extra tools
# secret-toolkit = { git = "https://github.com/scrtlabs/secret-toolkit", tag = "v0.8.0" }
Expand All @@ -42,3 +52,4 @@ cosmwasm-schema = "1.0.0"

[[bin]]
name = "schema"
required-features = ["schema"]
5 changes: 3 additions & 2 deletions subscription-manager/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ _build-mainnet:
.PHONY: build-mainnet-reproducible
build-mainnet-reproducible:
docker run --rm -v "$$(pwd)":/contract \
--mount type=volume,source="$$(basename "$$(pwd)")_cache",target=/contract/target \
--mount type=volume,source="$$(basename "$$(pwd)")_cache",target=/code/target \
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
ghcr.io/scrtlabs/localsecret:v1.6.0-rc.3
mr7uca/wasm-contract-optimizer:0.0.10


.PHONY: compress-wasm
compress-wasm:
Expand Down
239 changes: 239 additions & 0 deletions subscription-manager/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
# Claive Subscription Manager Contract

This repository contains a CosmWasm smart contract for managing subscriptions on Secret Network. The contract provides functionality to register and remove subscribers, manage API keys, and includes admin management features. Secret Network's privacy features are utilized to keep subscriber data confidential and secure.

---

## Overview

The Claive Subscription Manager Contract is designed for subscription-based use cases, where an admin manages subscribers using their public keys. The contract keeps track of registered subscribers and API keys, ensuring that only authorized admins can manage them.

### Contract State

The contract stores:

- **Admin Address**: The account that has permission to register or remove subscribers, manage API keys, and change admin rights.
- **Subscribers**: A mapping from a public key to the subscriber's status (active or inactive).
- **API Keys**: A mapping of API keys used for external access control. API keys can optionally be associated with an identity, and they are stored securely.

### Methods

#### 1. **Instantiate**

- Initializes the contract and sets the admin to the sender's address.

#### 2. **Execute**

- `RegisterSubscriber`: Adds a new subscriber using their public key. Only callable by the admin.
- `RemoveSubscriber`: Removes a subscriber using their public key. Only callable by the admin.
- `SetAdmin`: Changes the admin to a new address. Only callable by the current admin.
- `AddApiKey`: Adds a new API key for access control, with an optional identity. Only callable by the admin.
- `RevokeApiKey`: Revokes an existing API key. The contract verifies its existence before removal. Only callable by the admin.

#### 3. **Query**

- `SubscriberStatusWithPermit`: Checks if a subscriber with the given public key is active. Requires a valid permit signed by the admin.
- `ApiKeysWithPermit`: Returns a list of all registered API keys (**hashed by sha-256**). Requires a valid permit signed by the admin.
- `ApiKeysByIdentityWithPermit`: Retrieves API keys associated with a given identity. Requires admin authorization.
- `GetAdmin`: Returns the current admin address.

---

## Prerequisites

To use and deploy this contract, you'll need:

- [**SecretCLI**](https://docs.scrt.network/secret-network-documentation/infrastructure/secret-cli) for interacting with the Secret Network.
- [**LocalSecret**](https://docs.scrt.network/secret-network-documentation/development/readme-1/setting-up-your-environment) for local testing and development.

Please refer to the documentation above to install and familiarize yourself with these tools.

---

## Step 1: Build the Contract

### Prerequisites

- **Rust** and **wasm-opt** must be installed.
- Add the wasm32-unknown-unknown target for Rust if you haven’t done so:

```bash
rustup target add wasm32-unknown-unknown
```

### Build Instructions

1. **Build the Contract**:

```bash
cargo build --release --target wasm32-unknown-unknown
```

2. **Optimize the Contract**:

```bash
wasm-opt -Oz -o contract-opt.wasm target/wasm32-unknown-unknown/release/claive_subscription_manager.wasm
```

3. **Compress the Contract**:

```bash
gzip -9 -c contract-opt.wasm > contract.wasm.gz
```

---

## Step 2: Deploy the Contract

### Prerequisites

- **SecretCLI** must be configured.

### Deploy Instructions

1. **Deploy the Contract**:

```bash
secretcli tx compute store contract.wasm.gz --gas 5000000 --from myWallet -y
```

2. **Get the code_id**:

```bash
secretcli query compute list-code
```

---

## Step 3: Instantiate the Contract

### Prerequisites

- You need the code_id from the previous step.

### Instantiate Instructions

1. **Instantiate the Contract**:

```bash
secretcli tx compute instantiate <code_id> '{}' --from myWallet --label subContract -y
```

2. **Get the Contract Address**:

```bash
secretcli query compute list-contract-by-code <code_id>
```

---

## Use Cases

### Use Case 1: Register a Subscriber

**Description**: Register a subscriber using their public key. Only the admin can perform this action.

#### Command

```bash
secretcli tx compute execute <contract_address> '{"register_subscriber":{"public_key":"subscriber_pub_key"}}' --from myWallet -y
```

---

### Use Case 2: Query Subscriber Status with Permit

**Description**: Check if a subscriber is active or not. Requires a valid permit signed by the admin.

#### Command

```bash
secretcli query compute query <contract_address> '{"subscriber_status_with_permit":{"public_key":"subscriber_pub_key","permit":<permit_json>}}'
```

---

### Use Case 3: Remove a Subscriber

**Description**: Remove a subscriber using their public key. Only the admin can perform this action.

#### Command

```bash
secretcli tx compute execute <contract_address> '{"remove_subscriber":{"public_key":"subscriber_pub_key"}}' --from myWallet -y
```

---

### Use Case 4: Set a New Admin

**Description**: Update the admin to a new public key. Only the current admin can perform this action.

#### Command

```bash
secretcli tx compute execute <contract_address> '{"set_admin":{"public_key":"new_admin_pub_key"}}' --from myWallet -y
```

---

### Use Case 5: Add an API Key

**Description**: Add a new API key for access control. Only the admin can perform this action. The API key is hashed using SHA-256 before storage.

#### Command

```bash
secretcli tx compute execute <contract_address> '{"add_api_key":{"api_key":"new_api_key"}}' --from myWallet -y
```

---

### Use Case 6: Revoke an API Key

**Description**: Revoke an existing API key. Only the admin can perform this action. The API key must be provided in plaintext, and the contract verifies its hash before removal.

#### Command

```bash
secretcli tx compute execute <contract_address> '{"revoke_api_key":{"api_key":"api_key_to_revoke"}}' --from myWallet -y
```

---

### Use Case 7: Query API Keys with Permit

**Description**: Retrieve the list of all registered API keys in **hashed format**. Requires a valid permit signed by the admin.

#### Command

```bash
secretcli query compute query <contract_address> '{"api_keys_with_permit":{"permit":<permit_json>}}'
```

---

### Use Case 8: Query API Keys by Identity with Permit

**Description**: Retrieve all **actual API keys** associated with a given identity. Requires a valid permit signed by the admin.

#### Command

```bash
secretcli query compute query <contract_address> '{"api_keys_by_identity_with_permit":{"identity":"some_identity","permit":<permit_json>}}'
```

---

### Use Case 9: Get Admin Address

**Description**: Retrieve the current admin address.

#### Command

```bash
secretcli query compute query <contract_address> '{"get_admin":{}}'
```

---

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import os
from time import sleep

from dotenv import load_dotenv
from secret_sdk.client.lcd import LCDClient
from secret_sdk.client.localsecret import LocalSecret, main_net_chain_id, test_net_chain_id
from secret_sdk.core.coins import Coins
from secret_sdk.key.mnemonic import MnemonicKey
from secret_sdk.protobuf.cosmos.tx.v1beta1 import BroadcastMode
from secret_sdk.util.contract import get_contract_events

load_dotenv()
chain_id = os.getenv('CHAIN_ID')
contract = os.getenv('CONTRACT_ADDRESS')
node_url = os.getenv('NODE_URL')
mnemonic = os.getenv('MNEMONIC')

print("chain_id: " + chain_id)
print("node_url: " + node_url)
print("contract: " + contract)
print("mnemonic: " + mnemonic)

mk = MnemonicKey(mnemonic=mnemonic)
secret = LCDClient(chain_id=chain_id, url=node_url)
wallet = secret.wallet(mk)

wallet_public_key = str(wallet.key.acc_address)

print("wallet_public_key: " + wallet_public_key)

contract_address = contract
sent_funds = Coins('100uscrt')

public_key = "subscriber"
handle_msg = {"remove_subscriber": {"public_key": public_key}}

t = wallet.execute_tx(
contract_addr=contract_address,
handle_msg=handle_msg,
transfer_amount=sent_funds,
)

assert t.code == 0, f"Transaction failed with code {t.code}: {t.rawlog}"
print("Transaction successful:", t.txhash)

sleep(5)

tx_info = secret.tx.tx_info(
tx_hash=t.txhash,
)
print("Transaction info:", tx_info)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import os
from dotenv import load_dotenv
from secret_sdk.client.lcd import LCDClient

# Загрузите переменные из .env файла
load_dotenv()

# Получите значения из переменных окружения
chain_id = os.getenv('CHAIN_ID')
node_url = os.getenv('NODE_URL')

# Используйте значения для создания LCDClient
secret = LCDClient(chain_id=chain_id, url=node_url)
height = secret.tendermint.block_info()['block']['header']['height']
print(height)
Loading