Skip to content

Commit

Permalink
[cmd] Upgrade airdrop command to use new TransferScripts::batch_peer_…
Browse files Browse the repository at this point in the history
…to_peer_v2 (#3264)

* [cmd] Upgrade airdrop command to use new TransferScripts::batch_peer_to_peer_v2.

* [cmd] Airdrop command support other Token check the users is accepted the token.

* fixup
  • Loading branch information
jolestar authored Mar 9, 2022
1 parent 0c20e57 commit 4cdccaa
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 24 deletions.
10 changes: 5 additions & 5 deletions cmd/airdrop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ Airdrop STC to starcoin users.
./target/debug/airdrop -i amount.csv
```

amount.csv should be in format: `address,auth_key,amount`. for example:
amount.csv should be in format: `address,amount`. for example:

```text
0x00009cc5c3d56231df33dd74cc4780f2,809828f0150fc07e15c6d27db607a56b00009cc5c3d56231df33dd74cc4780f2,1000
0x0026241a238ede23f1d9e18421c0185d,b6a80ebbf065eb1e8964b83f2c8fad040026241a238ede23f1d9e18421c0185d,1000
0x00342e46b92e499108f2e5fbd3e44227,65f53b52ab2b12b683678cb83fa03bd200342e46b92e499108f2e5fbd3e44227,999
0x0034914ea62e78ac86ddf99f9240c7c5,232f6bd40fc5efb59f27ff2003323b150034914ea62e78ac86ddf99f9240c7c5,99
0x00009cc5c3d56231df33dd74cc4780f2,1000
0x0026241a238ede23f1d9e18421c0185d,1000
0x00342e46b92e499108f2e5fbd3e44227,999
0x0034914ea62e78ac86ddf99f9240c7c5,99
```
106 changes: 87 additions & 19 deletions cmd/airdrop/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,53 +1,110 @@
// Copyright (c) The Starcoin Core Contributors
// SPDX-License-Identifier: Apache-2.0

use anyhow::Result;
use bcs_ext::BCSCodec;
use clap::Parser;
use jsonrpc_core_client::{RpcChannel, RpcError};
use serde::Deserialize;
use starcoin_crypto::{HashValue, ValidCryptoMaterialStringExt};
use starcoin_rpc_api::types::{TransactionInfoView, TransactionStatusView};
use starcoin_rpc_api::types::{ResourceView, TransactionInfoView, TransactionStatusView};
use starcoin_rpc_api::{
chain::ChainClient, node::NodeClient, state::StateClient, txpool::TxPoolClient,
};
use starcoin_types::access_path::{AccessPath, DataPath};
use starcoin_types::account_address::AccountAddress;
use starcoin_types::account_config::{
account_struct_tag, genesis_address, stc_type_tag, AccountResource,
};
use starcoin_types::account_config::{account_struct_tag, genesis_address, AccountResource};
use starcoin_types::genesis_config::ChainId;
use starcoin_types::identifier::Identifier;
use starcoin_types::language_storage::ModuleId;
use starcoin_types::transaction::authenticator::{AccountPrivateKey, AuthenticationKey};
use starcoin_types::transaction::authenticator::AccountPrivateKey;
use starcoin_types::transaction::{RawUserTransaction, ScriptFunction};
use starcoin_vm_types::account_config::auto_accept_token::AutoAcceptToken;
use starcoin_vm_types::account_config::{stc_type_tag, BalanceResource, STC_TOKEN_CODE};
use starcoin_vm_types::language_storage::{StructTag, TypeTag};
use starcoin_vm_types::move_resource::MoveResource;
use starcoin_vm_types::token::token_code::TokenCode;
use starcoin_vm_types::transaction::SignedUserTransaction;
use starcoin_vm_types::value::MoveValue;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;

#[derive(Parser, Debug, Clone)]
#[clap(version = "0.1.0", author = "Starcoin Core Dev <dev@starcoin.org>")]
pub struct Options {
#[clap(long, default_value = "http://localhost:9850")]
/// starcoin node http rpc url
node_url: String,
#[clap(short = 'i')]
/// airdrop input csv. columns: `address,auth_key,amount`
/// airdrop input csv. columns: `address,amount`
airdrop_file: PathBuf,
#[clap(short, long, default_value = "32")]
/// batch size to do transfer
batch_size: usize,

#[clap(
short = 't',
long = "token-code",
name = "token-code",
help = "token's code to drop, for example: 0x1::STC::STC, default is STC."
)]
token_code: Option<TokenCode>,
}

#[derive(Copy, Clone, Debug, Deserialize)]
pub struct AirdropInfo {
address: AccountAddress,
auth_key: AuthenticationKey,
amount: u128,
}

fn map_rpc_error(err: RpcError) -> anyhow::Error {
anyhow::anyhow!(format!("{}", err))
}

async fn is_accept_token(
address: AccountAddress,
token_type: StructTag,
client: &StateClient,
) -> Result<bool> {
let account = client
.get_resource(address, AccountResource::struct_tag().into(), None)
.await
.map_err(map_rpc_error)?;

// if account do not exist on chain, will auto create when transfer token to the account.
if account.is_none() {
return Ok(true);
}

let balance = client
.get_resource(
address,
BalanceResource::struct_tag_for_token(token_type).into(),
None,
)
.await
.map_err(map_rpc_error)?;

if balance.is_some() {
return Ok(true);
}

let auto_accept_token: Option<ResourceView> = client
.get_resource(address, AutoAcceptToken::struct_tag().into(), None)
.await
.map_err(map_rpc_error)?;

let auto_accept = match auto_accept_token {
Some(view) => {
let auto_accept_token = view.decode::<AutoAcceptToken>()?;
auto_accept_token.enable()
}
None => false,
};
Ok(auto_accept)
}

#[tokio::main]
async fn main() -> Result<()> {
let options: Options = Options::parse();
Expand All @@ -63,13 +120,27 @@ async fn main() -> Result<()> {
let node_client = NodeClient::from(channel.clone());
let chain_id: u8 = chain_client.id().await.map_err(map_rpc_error)?.id;

let token_type: StructTag = options
.token_code
.unwrap_or_else(|| STC_TOKEN_CODE.clone())
.try_into()?;
let is_stc = stc_type_tag().eq(&TypeTag::Struct(token_type.clone()));

let airdrop_infos: Vec<AirdropInfo> = {
let mut csv_reader = csv::ReaderBuilder::default()
.has_headers(false)
.from_path(airdrop_file.as_path())?;
let mut leafs = Vec::with_capacity(4096);
for record in csv_reader.deserialize() {
let data = record?;
let data: AirdropInfo = record?;
if !is_stc && !is_accept_token(data.address, token_type.clone(), &state_client).await? {
println!(
"{} does not accepted the token {}, skip.",
data.address,
token_type.to_string()
);
continue;
}
leafs.push(data);
}
leafs
Expand All @@ -91,7 +162,12 @@ async fn main() -> Result<()> {
AccountAddress::from_str(address.as_str())?
}
};
println!("Will act as sender {}", sender);

println!(
"Will act as sender {}, token: {}",
sender,
token_type.to_string()
);

// read from onchain
let account_sequence_number = {
Expand All @@ -111,13 +187,6 @@ async fn main() -> Result<()> {
.map(MoveValue::Address)
.collect(),
);
let auth_keys = MoveValue::Vector(
airdrops
.iter()
.map(|info| info.auth_key)
.map(|v| MoveValue::vector_u8(v.to_vec()))
.collect(),
);
let amounts = MoveValue::Vector(
airdrops
.iter()
Expand All @@ -131,11 +200,10 @@ async fn main() -> Result<()> {
genesis_address(),
Identifier::new("TransferScripts").unwrap(),
),
Identifier::new("batch_peer_to_peer").unwrap(),
vec![stc_type_tag()],
Identifier::new("batch_peer_to_peer_v2").unwrap(),
vec![token_type.clone().into()],
vec![
addresses.simple_serialize().unwrap(),
auth_keys.simple_serialize().unwrap(),
amounts.simple_serialize().unwrap(),
],
);
Expand Down
43 changes: 43 additions & 0 deletions vm/types/src/account_config/resources/auto_accept_token.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::token::token_code::TokenCode;
use crate::{
account_config::constants::{ACCOUNT_MODULE_NAME, CORE_CODE_ADDRESS},
move_resource::MoveResource,
};
use move_core_types::language_storage::{StructTag, TypeTag};
use serde::{Deserialize, Serialize};

/// The AutoAcceptToken resource held under an account.
#[derive(Debug, Serialize, Deserialize)]
pub struct AutoAcceptToken {
enable: bool,
}

impl AutoAcceptToken {
pub fn enable(&self) -> bool {
self.enable
}

/// Get token code from Balance StructTag, return None if struct tag is not a valid Balance StructTag
pub fn token_code(struct_tag: &StructTag) -> Option<TokenCode> {
if struct_tag.address == CORE_CODE_ADDRESS
&& struct_tag.module.as_str() == Self::MODULE_NAME
&& struct_tag.name.as_str() == Self::STRUCT_NAME
{
if let Some(TypeTag::Struct(token_tag)) = struct_tag.type_params.get(0) {
Some(token_tag.clone().into())
} else {
None
}
} else {
None
}
}
}

impl MoveResource for AutoAcceptToken {
const MODULE_NAME: &'static str = ACCOUNT_MODULE_NAME;
const STRUCT_NAME: &'static str = "AutoAcceptToken";
}
1 change: 1 addition & 0 deletions vm/types/src/account_config/resources/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

pub mod account;
pub mod auto_accept_token;
pub mod balance;
pub mod key_rotation_capability;
pub mod module_upgrade_strategy;
Expand Down

0 comments on commit 4cdccaa

Please sign in to comment.