Skip to content

Commit

Permalink
A doc with examples of commands (#913)
Browse files Browse the repository at this point in the history
* build three commands

* updated docs and passed all example commands

* wronge testnet id and path

* error handling

* Shorter about near-cli

Co-authored-by: Serhii Volovyk <[email protected]>

* Naming

Co-authored-by: Serhii Volovyk <[email protected]>

* Change caller id

Co-authored-by: Serhii Volovyk <[email protected]>

* pass correct testnet contract id

---------

Co-authored-by: AlexKushnir1 <alex@alex-Precision-7510>
Co-authored-by: Serhii Volovyk <[email protected]>
  • Loading branch information
3 people authored Nov 8, 2024
1 parent 2cb54bc commit 144fae5
Show file tree
Hide file tree
Showing 6 changed files with 327 additions and 2 deletions.
8 changes: 7 additions & 1 deletion API.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,10 @@ For more details check `User contract API` impl block in the [chain-signatures/c

# Environments
1. Mainnet: `v1.signer`
2. Testnet: `v1.sigenr-prod.testnet`
2. Testnet: `v1.sigenr-prod.testnet`

# Interact using NEAR CLI

There is an `Example Commands` in the [chain-signatures/contract/example.md](./chain-signature/contract/EXAMPLE.md) file.

These commands were tested on [near-cli](https://github.com/near/near-cli) 4.0.0.
49 changes: 49 additions & 0 deletions chain-signatures/contract/EXAMPLE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Iteracting with contract using NEAR CLI
All data is fake and used for example purposes
It's necessary to update script after contract API changes
## User contract API

near call v1.signer-dev.testnet sign '{"request":{"key_version":0,"path":"test","payload":[12,1,2,0,4,5,6,8,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,44]}}' --accountId alexkushnir.testnet --gas 300000000000000 --deposit 1

near view v1.signer-dev.testnet public_key

near view v1.signer-dev.testnet derived_public_key {"path":"test","predecessor":"alexkushnir.testnet"}

near view v1.signer-dev.testnet latest_key_version

near view v1.signer-dev.testnet experimental_signature_deposit


## Node API

near call v1.signer-dev.testnet respond '{"request":{"epsilon":{"scalar":"72DB59A313FA266B1C3B40F20325C9023DDC564E7790363BCC2AE76580339648"},"payload_hash":{"scalar":"05FCB4470106774DCC5A3C7689FD2917C15AB81B6FA44960843E6389780A5364"}},"response":{"big_r":{"affine_point":"02EC7FA686BB430A4B700BDA07F2E07D6333D9E33AEEF270334EB2D00D0A6FEC6C"},"recovery_id":0,"s":{"scalar":"20F90C540EE00133C911EA2A9ADE2ABBCC7AD820687F75E011DFEEC94DB10CD6"}}}' --accountId alexkushnir.testnet --gas 300000000000000

near call v1.signer-dev.testnet join '{"cipher_pk":[59,105,187,93,147,173,85,119,242,237,171,117,87,221,135,181,28,120,239,58,50,198,137,77,219,16,151,195,93,140,92,88],"sign_pk":"ed25519:J75xXmF7WUPS3xCm3hy2tgwLCKdYM1iJd4BWF8sWVnae","url":"http://localhost:3030"}' --accountId alexkushnir.testnet --gas 300000000000000

near call v1.signer-dev.testnet vote_join '{"candidate":"alexkushnir.testnet"}' --accountId alexkushnir.testnet --gas 300000000000000

near call v1.signer-dev.testnet vote_leave '{"kick":"alexkushnir.testnet"}' --accountId alexkushnir.testnet --gas 300000000000000

near call v1.signer-dev.testnet vote_pk '{"public_key": ed25519:J75xXmF7WUPS3xCm3hy2tgwLCKdYM1iJd4BWF8sWVnae}' --accountId alexkushnir.testnet --gas 300000000000000

near call v1.signer-dev.testnet vote_reshared '{"epoch": 1}' --accountId alexkushnir.testnet --gas 300000000000000

near call v1.signer-dev.testnet propose_update --base64 "AAHgkwQAAAAAAADdbQAAAAAAAgAAAEAAAAAABAAAAABAAMAnCQAAAAAAAAAAAAACAAAAACAAyK8AAAAAAAAAAAAAyK8AAAAAAABADQMAAAAAAABcJgUAAAAAAAAAAAAAAAAAAAAA" --accountId alexkushnir.testnet --gas 300000000000000

near call v1.signer-dev.testnet vote_update '{"id": 0}' --accountId alexkushnir.testnet --gas 300000000000000


## Contract developer helper API

near call v1.signer-dev.testnet init '{"candidates":{"candidates":{"alice.near":{"account_id":"alice.near","cipher_pk":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"sign_pk":"ed25519:J75xXmF7WUPS3xCm3hy2tgwLCKdYM1iJd4BWF8sWVnae","url":"127.0.0.1"},"bob.near":{"account_id":"bob.near","cipher_pk":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"sign_pk":"ed25519:J75xXmF7WUPS3xCm3hy2tgwLCKdYM1iJd4BWF8sWVnae","url":"127.0.0.1"},"caesar.near":{"account_id":"caesar.near","cipher_pk":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"sign_pk":"ed25519:J75xXmF7WUPS3xCm3hy2tgwLCKdYM1iJd4BWF8sWVnae","url":"127.0.0.1"}}},"threshold":1}' --accountId alexkushnir.testnet --gas 300000000000000

near call v1.signer-dev.testnet init_running '{"epoch":0,"participants":{"account_to_participant_id":{"alice.near":0,"bob.near":1,"caesar.near":2},"next_id":3,"participants":{"alice.near":{"account_id":"alice.near","cipher_pk":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"sign_pk":"ed25519:J75xXmF7WUPS3xCm3hy2tgwLCKdYM1iJd4BWF8sWVnae","url":"127.0.0.1"},"bob.near":{"account_id":"bob.near","cipher_pk":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"sign_pk":"ed25519:J75xXmF7WUPS3xCm3hy2tgwLCKdYM1iJd4BWF8sWVnae","url":"127.0.0.1"},"caesar.near":{"account_id":"caesar.near","cipher_pk":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"sign_pk":"ed25519:J75xXmF7WUPS3xCm3hy2tgwLCKdYM1iJd4BWF8sWVnae","url":"127.0.0.1"}}},"public_key":"ed25519:J75xXmF7WUPS3xCm3hy2tgwLCKdYM1iJd4BWF8sWVnae","threshold":2}' --accountId alexkushnir.testnet --gas 300000000000000

near view v1.signer-dev.testnet migrate

near view v1.signer-dev.testnet state

near view v1.signer-dev.testnet config

near view v1.signer-dev.testnet version

1 change: 1 addition & 0 deletions integration-tests/chain-signatures/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion integration-tests/chain-signatures/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
thiserror = "1"
url = { version = "2.4.0", features = ["serde"] }
web3 = "0.19.0"
deadpool-redis = "0.18.0"

# crypto dependencies
Expand All @@ -41,6 +42,7 @@ k256 = { version = "0.13.1", features = ["sha256", "ecdsa", "serde"] }
near-account-id = "1"
near-crypto = "0.26.0"
near-fetch = "0.6.0"
near-sdk = "5.2.1"
near-jsonrpc-client = "0.13.0"
near-primitives = "0.26.0"
near-lake-framework = { git = "https://github.com/near/near-lake-framework-rs", branch = "node/2.3.0" }
Expand All @@ -60,7 +62,6 @@ test-log = { version = "0.2.12", features = ["log", "trace"] }
# crypto dependencies
ecdsa = "0.16.9"
ethers-core = "2.0.13"
web3 = "0.19.0"
secp256k1 = "0.28.2"

[build-dependencies]
Expand Down
164 changes: 164 additions & 0 deletions integration-tests/chain-signatures/src/commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
use std::str::FromStr;

use crypto_shared::{ScalarExt, SerializableAffinePoint, SerializableScalar, SignatureResponse};
use k256::Scalar;
use mpc_contract::{
config::Config,
primitives::{CandidateInfo, Candidates, Participants, SignRequest, SignatureRequest},
update::ProposeUpdateArgs,
};
use mpc_keys::hpke;
use near_account_id::AccountId;
use near_primitives::borsh;
use near_sdk::PublicKey;
use serde_json::json;

const PAYLOAD: [u8; 32] = [
12, 1, 2, 0, 4, 5, 6, 8, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 44,
];

const SIGN_PK: &str = "ed25519:J75xXmF7WUPS3xCm3hy2tgwLCKdYM1iJd4BWF8sWVnae";

pub fn sing_command(contract_id: &AccountId, caller_id: &AccountId) -> anyhow::Result<String> {
let sign_request = SignRequest {
payload: PAYLOAD,
path: "test".into(),
key_version: 0,
};

let request_json = format!(
"'{}'",
serde_json::to_string(&json!({"request": sign_request}))?
);

Ok(format!(
"near call {} sign {} --accountId {} --gas 300000000000000 --deposit 1",
contract_id, request_json, caller_id
))
}

pub fn respond_command(contract_id: &AccountId, caller_id: &AccountId) -> anyhow::Result<String> {
let payload_hashed = web3::signing::keccak256(&PAYLOAD);

let request = SignatureRequest::new(
Scalar::from_bytes(payload_hashed)
.ok_or_else(|| anyhow::anyhow!("Failed to convert bytes to Scalar"))?,
caller_id,
"test",
);

let big_r = serde_json::from_value(
"02EC7FA686BB430A4B700BDA07F2E07D6333D9E33AEEF270334EB2D00D0A6FEC6C".into(),
)?; // Fake BigR
let s = serde_json::from_value(
"20F90C540EE00133C911EA2A9ADE2ABBCC7AD820687F75E011DFEEC94DB10CD6".into(),
)?; // Fake S

let response = SignatureResponse {
big_r: SerializableAffinePoint {
affine_point: big_r,
},
s: SerializableScalar { scalar: s },
recovery_id: 0,
};

let request_json = format!(
"'{}'",
serde_json::to_string(&json!({"request": request, "response": response})).unwrap()
);

Ok(format!(
"near call {} respond {} --accountId {} --gas 300000000000000",
contract_id, request_json, caller_id
))
}

pub fn join_command(contract_id: &AccountId, caller_id: &AccountId) -> anyhow::Result<String> {
let url = "http://localhost:3030";
let (_, cipher_pk) = hpke::generate();
let sign_pk = PublicKey::from_str(SIGN_PK)?;

let join_json = format!(
"'{}'",
serde_json::to_string(&json!({"url": url, "cipher_pk": cipher_pk, "sign_pk": sign_pk}))?
);

Ok(format!(
"near call {} join {} --accountId {} --gas 300000000000000",
contract_id, join_json, caller_id
))
}

pub fn proposed_updates_command(
contract_id: &AccountId,
caller_id: &AccountId,
) -> anyhow::Result<String> {
let args = ProposeUpdateArgs {
code: None,
config: Some(Config::default()),
};

let borsh_args = borsh::to_vec(&args)?;

let base64_encoded = near_primitives::serialize::to_base64(borsh_args.as_slice());

Ok(format!(
"near call {} propose_update --base64 {:?} --accountId {} --gas 300000000000000",
contract_id, base64_encoded, caller_id
))
}

pub fn init_command(contract_id: &AccountId, caller_id: &AccountId) -> anyhow::Result<String> {
let threshold: usize = 1;
let candidates: Candidates = dummy_candidates();

let init_json = format!(
"'{}'",
serde_json::to_string(&json!({"threshold": threshold, "candidates": candidates}))?
);

Ok(format!(
"near call {} init {} --accountId {} --gas 300000000000000",
contract_id, init_json, caller_id
))
}

pub fn init_running_command(
contract_id: &AccountId,
caller_id: &AccountId,
) -> anyhow::Result<String> {
let init_running_json = format!(
"'{}'",
serde_json::to_string(
&json!({"epoch": 0, "participants": Participants::from(dummy_candidates()), "threshold": 2,"public_key": PublicKey::from_str(SIGN_PK)? })
)?
);

Ok(format!(
"near call {} init_running {} --accountId {} --gas 300000000000000",
contract_id, init_running_json, caller_id
))
}

pub fn dummy_candidates() -> Candidates {
let mut candidates = Candidates::new();
let names: Vec<AccountId> = vec![
"alice.near".parse().unwrap(),
"bob.near".parse().unwrap(),
"caesar.near".parse().unwrap(),
];

for account_id in names {
candidates.insert(
account_id.clone(),
CandidateInfo {
account_id,
url: "127.0.0.1".into(),
cipher_pk: [0; 32],
sign_pk: PublicKey::from_str(SIGN_PK).unwrap(),
},
);
}
candidates
}
104 changes: 104 additions & 0 deletions integration-tests/chain-signatures/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
use std::fs::File;
use std::io::Write;
use std::str::FromStr;
use std::vec;

use clap::Parser;
use integration_tests_chain_signatures::containers::DockerClient;
use integration_tests_chain_signatures::{dry_run, run, utils, MultichainConfig};
use near_account_id::AccountId;
use near_crypto::PublicKey;
use serde_json::json;
use tokio::signal;
use tracing_subscriber::EnvFilter;

mod commands;

#[derive(Parser, Debug)]
enum Cli {
/// Spin up dependent services and mpc nodes
Expand All @@ -15,6 +25,8 @@ enum Cli {
},
/// Spin up dependent services but not mpc nodes
DepServices,
/// Generate example commands to interact with the contract
ContractCommands,
}

#[tokio::main]
Expand Down Expand Up @@ -79,6 +91,98 @@ async fn main() -> anyhow::Result<()> {
println!("Received Ctrl-C");
println!("Stopped dependency services");
}
Cli::ContractCommands => {
println!("Building a doc with example commands");
let path = "../../chain-signatures/contract/EXAMPLE.md";
let mut file = File::create(path)?;
let mut doc: Vec<String> = vec![];
let contract_account_id = AccountId::from_str("v1.signer-dev.testnet")?;
let caller_account_id = AccountId::from_str("caller.testnet")?;
let public_key: PublicKey =
"ed25519:J75xXmF7WUPS3xCm3hy2tgwLCKdYM1iJd4BWF8sWVnae".parse()?;

doc.push(
"# Iteracting with contract using NEAR CLI\nAll data is fake and used for example purposes\nIt's necessary to update script after contract API changes\n## User contract API"
.to_string()
);

doc.push(commands::sing_command(
&contract_account_id,
&caller_account_id,
)?);
doc.push(format!("near view {} public_key", contract_account_id));

doc.push(format!(
"near view {} derived_public_key {}",
contract_account_id,
serde_json::to_string(&json!({"path": "test","predecessor": caller_account_id}))?
));

doc.push(format!(
"near view {} latest_key_version",
contract_account_id
));

doc.push(format!(
"near view {} experimental_signature_deposit",
contract_account_id
));

doc.push(format!(
"\n## Node API\n\n{}\n\n{}",
commands::respond_command(&contract_account_id, &caller_account_id,)?,
commands::join_command(&contract_account_id, &caller_account_id,)?
));

doc.push(format!(
"near call {} vote_join '{{\"candidate\":\"{}\"}}' --accountId {} --gas 300000000000000",
contract_account_id, caller_account_id, caller_account_id
));

doc.push(format!(
"near call {} vote_leave '{{\"kick\":\"{}\"}}' --accountId {} --gas 300000000000000",
contract_account_id, caller_account_id, caller_account_id
));

doc.push(format!(
"near call {} vote_pk '{{\"public_key\": {}}}' --accountId {} --gas 300000000000000",
contract_account_id, public_key, caller_account_id
));

doc.push(format!(
"near call {} vote_reshared '{{\"epoch\": 1}}' --accountId {} --gas 300000000000000",
contract_account_id, caller_account_id
));

doc.push(commands::proposed_updates_command(
&contract_account_id,
&caller_account_id,
)?);

doc.push(format!(
"near call {} vote_update '{{\"id\": 0}}' --accountId {} --gas 300000000000000",
contract_account_id, caller_account_id
));

doc.push(format!(
"\n## Contract developer helper API\n\n{}\n\n{}",
commands::init_command(&contract_account_id, &caller_account_id,)?,
commands::init_running_command(&contract_account_id, &caller_account_id,)?
));

doc.push(format!("near view {} migrate", contract_account_id));

doc.push(format!("near view {} state", contract_account_id));

doc.push(format!("near view {} config", contract_account_id));

doc.push(format!("near view {} version", contract_account_id));

for arg in doc {
file.write_all(arg.as_bytes())?;
file.write_all("\n\n".as_bytes())?;
}
}
}

Ok(())
Expand Down

0 comments on commit 144fae5

Please sign in to comment.