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

Problem: e2ee module not up to date #1610

Merged
merged 17 commits into from
Sep 27, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* [#1592](https://github.com/crypto-org-chain/cronos/pull/1592) Change the default parallelism of the block-stm to minimum between GOMAXPROCS and NumCPU
* [#1600](https://github.com/crypto-org-chain/cronos/pull/1600) Update ethermint to avoid unnecessary block result in header related api call.
* [#1606](https://github.com/crypto-org-chain/cronos/pull/1606) Fix pebbledb support.
* [#]() Sync e2ee module with v1.3.x branch.
yihuang marked this conversation as resolved.
Show resolved Hide resolved

*Sep 13, 2024*

Expand Down
24 changes: 20 additions & 4 deletions integration_tests/cosmoscli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1811,7 +1811,7 @@ def query_bank_send(self, *denoms):
)
).get("send_enabled", [])

def query_e2ee_key(self, address):
def query_e2ee_key(self, address):
yihuang marked this conversation as resolved.
Show resolved Hide resolved
return json.loads(
self.raw(
"q",
Expand Down Expand Up @@ -1853,10 +1853,13 @@ def register_e2ee_key(self, key, **kwargs):
rsp = self.event_query_tx_for(rsp["txhash"])
return rsp

def keygen(self, **kwargs):
def e2ee_keygen(self, **kwargs):
return self.raw("e2ee", "keygen", home=self.data_dir, **kwargs).strip().decode()

def encrypt(self, input, *recipients, **kwargs):
def e2ee_pubkey(self, **kwargs):
return self.raw("e2ee", "pubkey", home=self.data_dir, **kwargs).strip().decode()

def e2ee_encrypt(self, input, *recipients, **kwargs):
return (
self.raw(
"e2ee",
Expand All @@ -1870,7 +1873,7 @@ def encrypt(self, input, *recipients, **kwargs):
.decode()
)

def decrypt(self, input, identity="e2ee-identity", **kwargs):
def e2ee_decrypt(self, input, identity="e2ee-identity", **kwargs):
return (
self.raw(
"e2ee",
Expand All @@ -1883,3 +1886,16 @@ def decrypt(self, input, identity="e2ee-identity", **kwargs):
.strip()
.decode()
)

def e2ee_encrypt_to_validators(self, input, **kwargs):
return (
self.raw(
"e2ee",
"encrypt-to-validators",
input,
home=self.data_dir,
**kwargs,
)
.strip()
.decode()
)
155 changes: 131 additions & 24 deletions integration_tests/test_e2ee.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,147 @@
def test_register(cronos):
import json

import pytest
from eth_utils import to_checksum_address
from hexbytes import HexBytes
from pystarport import ports

from .network import Cronos
from .utils import ADDRS, bech32_to_eth, wait_for_new_blocks, wait_for_port


def test_register(cronos: Cronos):
cli = cronos.cosmos_cli()
pubkey0 = cli.keygen(keyring_name="key0")
rsp = cli.register_e2ee_key(pubkey0 + "malformed", _from="validator")
assert rsp["code"] != 0
assert "malformed recipient" in rsp["raw_log"]
pubkey0 = cli.e2ee_keygen(keyring_name="key0")
with pytest.raises(AssertionError) as exc:
cli.register_e2ee_key(pubkey0 + "malformed", _from="validator")
assert "malformed recipient" in str(exc.value)
assert not cli.query_e2ee_key(cli.address("validator"))


def gen_validator_identity(cronos: Cronos):
for i in range(len(cronos.config["validators"])):
cli = cronos.cosmos_cli(i)
if cli.query_e2ee_key(cli.address("validator")):
return
pubkey = cli.e2ee_keygen()
assert cli.e2ee_pubkey() == pubkey
cli.register_e2ee_key(pubkey, _from="validator")
assert cli.query_e2ee_key(cli.address("validator")) == pubkey

cronos.supervisorctl("restart", f"cronos_777-1-node{i}")

wait_for_new_blocks(cronos.cosmos_cli(), 1)


yihuang marked this conversation as resolved.
Show resolved Hide resolved
def test_encrypt_decrypt(cronos):
yihuang marked this conversation as resolved.
Show resolved Hide resolved
cli = cronos.cosmos_cli()
gen_validator_identity(cronos)

# gen two keys for two accounts
pubkey0 = cli.keygen(keyring_name="key0")
cli.register_e2ee_key(pubkey0, _from="validator")
assert cli.query_e2ee_key(cli.address("validator")) == pubkey0
pubkey1 = cli.keygen(keyring_name="key1")
cli.register_e2ee_key(pubkey1, _from="community")
cli0 = cronos.cosmos_cli()
cli1 = cronos.cosmos_cli(1)

# query in batch
assert cli.query_e2ee_keys(cli.address("validator"), cli.address("community")) == [
pubkey0,
pubkey1,
]
assert (
len(
cli0.query_e2ee_keys(
cli0.address("validator"),
cli1.address("validator"),
)
)
== 2
)

# prepare data file to encrypt
content = "Hello World!"
plainfile = cli.data_dir / "plaintext"
plainfile = cli0.data_dir / "plaintext"
plainfile.write_text(content)

cipherfile = cli.data_dir / "ciphertext"
cli.encrypt(
cipherfile = cli0.data_dir / "ciphertext"
cli0.e2ee_encrypt(
plainfile,
cli.address("validator"),
cli.address("community"),
cli0.address("validator"),
cli1.address("validator"),
output=cipherfile,
)

assert cli.decrypt(cipherfile, identity="key0") == content
assert cli.decrypt(cipherfile, identity="key1") == content
assert cli0.e2ee_decrypt(cipherfile) == content
assert cli1.e2ee_decrypt(cipherfile) == content


def encrypt_to_validators(cli, content):
blocklist = json.dumps(content)
plainfile = cli.data_dir / "plaintext"
plainfile.write_text(blocklist)
cipherfile = cli.data_dir / "ciphertext"
cli.e2ee_encrypt_to_validators(plainfile, output=cipherfile)
rsp = cli.store_blocklist(cipherfile, _from="validator")
assert rsp["code"] == 0, rsp["raw_log"]


def test_block_list(cronos):
gen_validator_identity(cronos)
cli = cronos.cosmos_cli()
user = cli.address("signer2")
# set blocklist
encrypt_to_validators(cli, {"addresses": [user]})

# normal tx works
cli.transfer(cli.address("validator"), user, "1basetcro")

# blocked tx can be included into mempool
rsp = cli.transfer(
user, cli.address("validator"), "1basetcro", event_query_tx=False
)
assert rsp["code"] == 0, rsp["raw_log"]

# but won't be included into block
txhash = rsp["txhash"]
with pytest.raises(AssertionError) as exc:
cli.event_query_tx_for(txhash)
assert "timed out waiting" in str(exc.value)
nonce = int(cli.query_account(user)["base_account"]["sequence"])

# clear blocklist
encrypt_to_validators(cli, {})

# the blocked tx should be unblocked now
wait_for_new_blocks(cli, 1)
assert nonce + 1 == int(cli.query_account(user)["base_account"]["sequence"])


def test_block_list_evm(cronos):
gen_validator_identity(cronos)
cli = cronos.cosmos_cli()
user = cli.address("signer2")
# set blocklist
encrypt_to_validators(cli, {"addresses": [user]})
tx = {
"from": to_checksum_address(bech32_to_eth(user)),
"to": ADDRS["community"],
"value": 1,
}
base_port = cronos.base_port(0)
wait_for_port(ports.evmrpc_ws_port(base_port))
w3 = cronos.w3
flt = w3.eth.filter("pending")
assert flt.get_new_entries() == []

txhash = w3.eth.send_transaction(tx).hex()
nonce = int(cli.query_account(user)["base_account"]["sequence"])
# check tx in mempool
assert HexBytes(txhash) in w3.eth.get_filter_changes(flt.filter_id)

# clear blocklist
encrypt_to_validators(cli, {})

# the blocked tx should be unblocked now
wait_for_new_blocks(cli, 1)
assert nonce + 1 == int(cli.query_account(user)["base_account"]["sequence"])
assert w3.eth.get_filter_changes(flt.filter_id) == []


def test_invalid_block_list(cronos):
cli = cronos.cosmos_cli()
cipherfile = cli.data_dir / "ciphertext"
cipherfile.write_text("{}")
with pytest.raises(AssertionError) as exc:
cli.store_blocklist(cipherfile, _from="validator")
assert "failed to read header" in str(exc.value)
2 changes: 2 additions & 0 deletions x/e2ee/client/cli/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ func E2EECommand() *cobra.Command {
KeygenCommand(),
EncryptCommand(),
DecryptCommand(),
EncryptToValidatorsCommand(),
PubKeyCommand(),
)

return cmd
Expand Down
3 changes: 2 additions & 1 deletion x/e2ee/client/cli/encrypt.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cli

import (
"context"
"errors"
"io"
"os"
Expand Down Expand Up @@ -40,7 +41,7 @@ func EncryptCommand() *cobra.Command {

// query encryption key from chain state
client := types.NewQueryClient(clientCtx)
rsp, err := client.Keys(clientCtx.CmdContext, &types.KeysRequest{
rsp, err := client.Keys(context.Background(), &types.KeysRequest{
Addresses: recs,
})
if err != nil {
Expand Down
110 changes: 110 additions & 0 deletions x/e2ee/client/cli/encrypt_to_validators.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package cli

import (
"context"
"fmt"
"io"
"os"

"filippo.io/age"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
sdk "github.com/cosmos/cosmos-sdk/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/spf13/cobra"

"github.com/crypto-org-chain/cronos/v2/x/e2ee/types"
)

func EncryptToValidatorsCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "encrypt-to-validators [input-file]",
Short: "Encrypt input file to one or multiple recipients",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

outputFile, err := cmd.Flags().GetString(flags.FlagOutput)
if err != nil {
return err
}

ctx := context.Background()

// get validator list
stakingClient := stakingtypes.NewQueryClient(clientCtx)
valsRsp, err := stakingClient.Validators(ctx, &stakingtypes.QueryValidatorsRequest{
Status: stakingtypes.BondStatusBonded,
})
if err != nil {
return err
}

recs := make([]string, len(valsRsp.Validators))
for i, val := range valsRsp.Validators {
bz, err := sdk.ValAddressFromBech32(val.OperatorAddress)
if err != nil {
return err
}
// convert to account address
recs[i] = sdk.AccAddress(bz).String()
}

// query encryption key from chain state
client := types.NewQueryClient(clientCtx)
rsp, err := client.Keys(context.Background(), &types.KeysRequest{
Addresses: recs,
})
if err != nil {
return err
}

recipients := make([]age.Recipient, len(recs))
for i, key := range rsp.Keys {
if len(key) == 0 {
fmt.Fprintf(os.Stderr, "missing encryption key for validator %s\n", recs[i])
continue
}

recipient, err := age.ParseX25519Recipient(key)
if err != nil {
fmt.Fprintf(os.Stderr, "invalid encryption key for validator %s, %v\n", recs[i], err)
continue
}
recipients[i] = recipient
}

inputFile := args[0]
var input io.Reader
if inputFile == "-" {
input = os.Stdin
} else {
f, err := os.Open(inputFile)
if err != nil {
return err
}
defer f.Close()
input = f
}

var output io.Writer
if outputFile == "-" {
output = os.Stdout
} else {
fp, err := os.Create(outputFile)
if err != nil {
return err
}
defer fp.Close()
output = fp
}
return encrypt(recipients, input, output)
},
}
f := cmd.Flags()
f.StringP(flags.FlagOutput, "o", "-", "output file (default stdout)")
return cmd
}
Loading
Loading