# Privacy-Preserving Transactions Module
## Introduction
In this tutorial, we'll create a module for privacy-preserving transactions using the Ignite CLI. This module will allow users to perform transactions while ensuring their details remain confidential.
## Step 1: Scaffold the Blockchain
First, scaffold a new blockchain. We'll call it privacynet:
```bash
ignite scaffold chain privacynet --no-module && cd privacynet
```
## Step 2: Scaffold the Privacy Module
```bash
ignite scaffold module privacy --dep bank
````
This command creates a new module named privacy and ensures it has dependencies on the bank module.
## Step 3: Define the Transaction Type
We need a type to represent a private transaction. Scaffold a `privateTx` type:
```bash
ignite scaffold type privateTx sender receiver amount hash --module privacy
```
## Step 4: Implementing Privacy Logic
**Scaffold the CreatePrivateTx Message**
```bash
ignite scaffold message createPrivateTx sender receiver amount --module privacy
```
**Add the `PrivateTxCountKey`**
In your `x/privacy/types/keys.go` file, add the `PrivateTxCountKey` to the constants:
```go=
const (
PrivateTxCountKey = "PrivateTxCount-value-"
)
```
**Hashing Transactions**
For privacy, the actual transaction details will be hashed. In `x/privacy/keeper/msg_server_create_private_tx.go`:
```go
package keeper
import (
"crypto/sha256"
"encoding/hex"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"privacynet/x/privacy/types"
)
func (k Keeper) CreatePrivateTx(ctx sdk.Context, sender, receiver, amount string) error {
// Hash transaction details
hash := sha256.Sum256([]byte(fmt.Sprintf("%s:%s:%d", sender, receiver, amount)))
hashStr := hex.EncodeToString(hash[:])
// Create PrivateTx object
privateTx := types.PrivateTx{
Sender: sender,
Receiver: receiver,
Amount: amount,
Hash: hashStr,
}
// Get the sender's address
sender, err := sdk.AccAddressFromBech32(msg.Sender)
if err != nil {
return nil, err
}
// Get the receiver's address
receiver, err := sdk.AccAddressFromBech32(msg.Receiver)
if err != nil {
return nil, err
}
// Create the coin amount to be transferred
amount := sdk.NewCoins(sdk.NewInt64Coin("token", msg.Amount))
// Use the bank keeper to send coins from the sender to the receiver
if err := k.bankKeeper.SendCoins(ctx, sender, receiver, amount); err != nil {
return nil, err
}
// Store PrivateTx
id := k.AppendPrivateTx(ctx, privateTx)
// Emit an event
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EventTypePrivateTx,
sdk.NewAttribute(types.AttributeKeyHash, hashStr),
sdk.NewAttribute(types.AttributeKeyPrivateTxID, fmt.Sprintf("%d", id)),
),
)
return nil
}
```
Add the `SendCoins` function to your `x/privacy/types/expected_keepers.go` BankKeeper Interfac:
```go
// BankKeeper defines the expected interface for the Bank module.
type BankKeeper interface {
SpendableCoins(context.Context, sdk.AccAddress) sdk.Coins
SendCoins(ctx context.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error
// Methods imported from bank should be defined here
}
```
**Privacy Keeper Methods**
In `x/privacy/keeper/keeper.go`, implement keeper methods to handle private transactions:
```go
package keeper
import (
"privacynet/x/privacy/types"
)
func (k Keeper) GetPrivateTxCount(ctx sdk.Context) uint64 {
storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
store := prefix.NewStore(storeAdapter, []byte{})
byteKey := types.KeyPrefix(types.PrivateTxCountKey)
bz := store.Get(byteKey)
if bz == nil {
return 0
}
return binary.BigEndian.Uint64(bz)
}
func (k Keeper) SetPrivateTxCount(ctx sdk.Context, count uint64) {
storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
store := prefix.NewStore(storeAdapter, []byte{})
byteKey := types.KeyPrefix(types.PrivateTxCountKey)
bz := make([]byte, 8)
binary.BigEndian.PutUint64(bz, count)
store.Set(byteKey, bz)
}
func (k Keeper) AppendPrivateTx(ctx sdk.Context, privateTx types.PrivateTx) uint64 {
// Create the store
storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
store := prefix.NewStore(storeAdapter, types.KeyPrefix(types.PrivateTxCountKey))
// Convert the PrivateTx ID to bytes
byteKey := make([]byte, 8)
binary.BigEndian.PutUint64(byteKey, k.GetPrivateTxCount(ctx))
// Marshal the PrivateTx
appenedValue := k.cdc.MustMarshal(&privateTx)
// Insert the PrivateTx in the store
store.Set(byteKey, appenedValue)
// Update the PrivateTx count
k.SetPrivateTxCount(ctx, k.GetPrivateTxCount(ctx)+1)
return k.GetPrivateTxCount(ctx) - 1
}
// Other necessary keeper methods
```
This method will store each private transaction in the module's store and maintain a count of all private transactions. It uses a binary encoding to store the transaction IDs.
The module is now capable of creating private transactions, where the details are hashed for confidentiality.