Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
YottaChain
YTBP
提交
e0a9a982
Y
YTBP
项目概览
YottaChain
/
YTBP
通知
0
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
Y
YTBP
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
e0a9a982
编写于
12月 13, 2017
作者:
B
Brian Johnson
提交者:
Kevin Heifner
1月 04, 2018
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Faucet_testnet_plugin to provide easy creation of test accounts on testnet.
(cherry picked from commit
ff0be1e1
)
上级
42cfdb02
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
413 addition
and
0 deletion
+413
-0
plugins/faucet_testnet_plugin/CMakeLists.txt
plugins/faucet_testnet_plugin/CMakeLists.txt
+16
-0
plugins/faucet_testnet_plugin/faucet_testnet_plugin.cpp
plugins/faucet_testnet_plugin/faucet_testnet_plugin.cpp
+368
-0
plugins/faucet_testnet_plugin/include/eosio/faucet_testnet_plugin/faucet_testnet_plugin.hpp
...ude/eosio/faucet_testnet_plugin/faucet_testnet_plugin.hpp
+29
-0
未找到文件。
plugins/faucet_testnet_plugin/CMakeLists.txt
0 → 100644
浏览文件 @
e0a9a982
file
(
GLOB HEADERS
"include/eosio/faucet_testnet_plugin/*.hpp"
)
add_library
(
faucet_testnet_plugin
faucet_testnet_plugin.cpp
${
HEADERS
}
)
target_link_libraries
(
faucet_testnet_plugin appbase fc http_plugin chain_plugin
)
target_include_directories
(
faucet_testnet_plugin PUBLIC
"
${
CMAKE_CURRENT_SOURCE_DIR
}
/include"
)
install
(
TARGETS
faucet_testnet_plugin
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
install
(
FILES
${
HEADERS
}
DESTINATION
"include/eosio/faucet_testnet_plugin"
)
plugins/faucet_testnet_plugin/faucet_testnet_plugin.cpp
0 → 100644
浏览文件 @
e0a9a982
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#include <eosio/faucet_testnet_plugin/faucet_testnet_plugin.hpp>
#include <eos/chain_plugin/chain_plugin.hpp>
#include <eos/utilities/key_conversion.hpp>
#include <fc/variant.hpp>
#include <fc/io/json.hpp>
#include <fc/exception/exception.hpp>
#include <fc/reflect/variant.hpp>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/algorithm/clamp.hpp>
#include <utility>
namespace
eosio
{
namespace
detail
{
struct
faucet_testnet_empty
{};
struct
faucet_testnet_keys
{
std
::
string
owner
;
std
::
string
active
;
};
struct
faucet_testnet_create_account_params
{
std
::
string
account
;
faucet_testnet_keys
keys
;
};
struct
faucet_testnet_create_account_alternates_response
{
std
::
vector
<
types
::
account_name
>
alternates
;
std
::
string
message
;
};
struct
faucet_testnet_create_account_rate_limited_response
{
std
::
string
message
;
};
}}
FC_REFLECT
(
eosio
::
detail
::
faucet_testnet_empty
,
);
FC_REFLECT
(
eosio
::
detail
::
faucet_testnet_keys
,
(
owner
)(
active
));
FC_REFLECT
(
eosio
::
detail
::
faucet_testnet_create_account_params
,
(
account
)(
keys
));
FC_REFLECT
(
eosio
::
detail
::
faucet_testnet_create_account_alternates_response
,
(
alternates
)(
message
));
FC_REFLECT
(
eosio
::
detail
::
faucet_testnet_create_account_rate_limited_response
,
(
message
));
namespace
eosio
{
using
namespace
eosio
::
chain
;
using
public_key_type
=
chain
::
public_key_type
;
using
key_pair
=
std
::
pair
<
std
::
string
,
std
::
string
>
;
using
results_pair
=
std
::
pair
<
uint32_t
,
fc
::
variant
>
;
#define CALL(api_name, api_handle, call_name, invoke_cb) \
{std::string("/v1/" #api_name "/" #call_name), \
[this](string, string body, url_response_callback response_cb) mutable { \
try { \
if (body.empty()) body = "{}"; \
const auto result = api_handle->invoke_cb(body); \
response_cb(result.first, fc::json::to_string(result.second)); \
} catch (fc::eof_exception& e) { \
error_results results{400, "Bad Request", e.to_string()}; \
response_cb(400, fc::json::to_string(results)); \
elog("Unable to parse arguments: ${args}", ("args", body)); \
} catch (fc::exception& e) { \
error_results results{500, "Internal Service Error", e.to_detail_string()}; \
response_cb(500, fc::json::to_string(results)); \
elog("Exception encountered while processing ${call}: ${e}", ("call", #api_name "." #call_name)("e", e)); \
} \
}}
static
std
::
vector
<
name
>
sort_names
(
std
::
vector
<
name
>&&
names
)
{
std
::
sort
(
names
.
begin
(),
names
.
end
()
);
auto
itr
=
std
::
unique
(
names
.
begin
(),
names
.
end
()
);
names
.
erase
(
itr
,
names
.
end
()
);
return
names
;
}
struct
faucet_testnet_plugin_impl
{
struct
create_faucet_account_alternate_results
{
std
::
vector
<
std
::
string
>
alternates
;
};
faucet_testnet_plugin_impl
(
appbase
::
application
&
app
)
:
_app
(
app
)
,
_timer
{
app
.
get_io_service
()}
{
}
void
timer_fired
()
{
_blocking_accounts
=
false
;
}
enum
http_return_codes
{
account_created
=
201
,
conflict_with_alternates
=
409
,
too_many_requests
=
429
};
class
extension
{
public:
explicit
extension
(
uint32_t
capacity
)
:
max_capacity
(
capacity
)
{
}
bool
increment
()
{
bool
incremented
=
false
;
// start incrementing from the end back to the begining
auto
rev_itr
=
ext
.
rbegin
();
for
(;
rev_itr
!=
ext
.
rend
();
++
rev_itr
)
{
// done once a character is incremented
if
(
increment
(
*
rev_itr
))
{
std
::
string
temp
(
1
,
*
rev_itr
);
incremented
=
true
;
break
;
}
}
const
bool
add_char
=
(
rev_itr
==
ext
.
rend
());
if
(
incremented
)
return
true
;
else
if
(
add_char
&&
ext
.
length
()
>=
max_capacity
)
{
return
false
;
}
if
(
!
ext
.
empty
())
{
do
{
--
rev_itr
;
*
rev_itr
=
init_char
;
std
::
string
temp
(
1
,
*
rev_itr
);
}
while
(
rev_itr
!=
ext
.
rbegin
());
}
if
(
add_char
)
{
ext
.
push_back
(
init_char
);
}
return
true
;
}
std
::
string
to_string
()
{
return
ext
;
}
// assuming 13 characters, which will mean inefficiencies if forced to search with 13th char, since only 1 through j is valid there
static
const
uint32_t
max_allowed_name_length
=
13
;
private:
// NOTE: code written expecting struct name to be using charmap of ".12345abcdefghijklmnopqrstuvwxyz"
bool
increment
(
char
&
c
)
{
std
::
string
temp
(
1
,
c
);
if
(
c
>=
'1'
&&
c
<
'5'
)
++
c
;
else
if
(
c
==
'5'
)
c
=
'a'
;
else
if
(
c
>=
'a'
&&
c
<
'z'
)
++
c
;
else
return
false
;
temp
.
assign
(
1
,
c
);
return
true
;
}
const
uint32_t
max_capacity
;
std
::
string
ext
;
const
char
init_char
=
'1'
;
};
struct
alternates
{
alternates
(
uint32_t
time_limit_microseconds
,
uint32_t
num_to_return
)
:
time_limit_microseconds
(
time_limit_microseconds
)
,
num_to_return
(
num_to_return
)
{
}
bool
done
()
{
return
names
.
size
()
>=
num_to_return
||
(
fc
::
time_point
::
now
()
-
start
).
count
()
>
time_limit_microseconds
;
}
const
fc
::
time_point
start
=
fc
::
time_point
::
now
();
const
uint32_t
time_limit_microseconds
;
const
uint32_t
num_to_return
;
std
::
vector
<
account_name
>
names
;
};
results_pair
find_alternates
(
const
std
::
string
&
new_account_name
)
{
alternates
alts
(
_create_alternates_time_limit_msec
*
1000
,
_create_alternates_to_return
);
std
::
string
suggestion
=
new_account_name
;
while
(
!
alts
.
done
()
&&
!
suggestion
.
empty
())
{
const
int32_t
extension_capacity
=
extension
::
max_allowed_name_length
-
suggestion
.
length
();
if
(
extension_capacity
>
0
)
{
extension
ext
(
extension_capacity
);
while
(
!
alts
.
done
()
&&
ext
.
increment
())
{
types
::
account_name
alt
;
// not externalizing all the name struct encoding, so need to handle cases where we
// construct an invalid encoded name
try
{
alt
=
suggestion
+
ext
.
to_string
();
}
catch
(
const
fc
::
assert_exception
&
)
{
continue
;
}
const
account_object
*
const
obj
=
database
().
find
<
account_object
,
by_name
>
(
alt
);
if
(
obj
==
nullptr
)
{
alts
.
names
.
push_back
(
alt
);
}
}
}
// drop the trailing character and try again
suggestion
.
pop_back
();
}
const
eosio
::
detail
::
faucet_testnet_create_account_alternates_response
response
{
alts
.
names
,
"Account name is already in use."
};
return
{
conflict_with_alternates
,
fc
::
variant
(
response
)
};
}
results_pair
create_account
(
const
std
::
string
&
new_account_name
,
const
eosio
::
types
::
public_key
&
owner_pub_key
,
const
eosio
::
types
::
public_key
&
active_pub_key
)
{
auto
creating_account
=
database
().
find
<
account_object
,
by_name
>
(
_create_account_name
);
EOS_ASSERT
(
creating_account
!=
nullptr
,
transaction_exception
,
"To create account using the faucet, must already have created account
\"
${a}
\"
"
,(
"a"
,
_create_account_name
));
auto
existing_account
=
database
().
find
<
account_object
,
by_name
>
(
new_account_name
);
if
(
existing_account
!=
nullptr
)
{
return
find_alternates
(
new_account_name
);
}
if
(
_blocking_accounts
)
{
eosio
::
detail
::
faucet_testnet_create_account_rate_limited_response
response
{
"Rate limit exceeded, the max is 1 request per "
+
fc
::
to_string
(
_create_interval_msec
)
+
" milliseconds. Come back later."
};
return
std
::
make_pair
(
too_many_requests
,
fc
::
variant
(
response
));
}
chain
::
chain_id_type
chainid
;
auto
&
plugin
=
_app
.
get_plugin
<
chain_plugin
>
();
plugin
.
get_chain_id
(
chainid
);
chain_controller
&
cc
=
plugin
.
chain
();
const
uint64_t
deposit
=
1
;
signed_transaction
trx
;
auto
memo
=
fc
::
variant
(
fc
::
time_point
::
now
()).
as_string
()
+
" "
+
fc
::
variant
(
fc
::
time_point
::
now
().
time_since_epoch
()).
as_string
();
//create "A" account
auto
owner_auth
=
chain
::
authority
{
1
,
{{
owner_pub_key
,
1
}},
{}};
auto
active_auth
=
chain
::
authority
{
1
,
{{
active_pub_key
,
1
}},
{}};
auto
recovery_auth
=
chain
::
authority
{
1
,
{},
{{{
_create_account_name
,
"active"
},
1
}}};
trx
.
scope
=
sort_names
({
_create_account_name
,
config
::
eos_contract_name
});
transaction_emplace_message
(
trx
,
config
::
eos_contract_name
,
std
::
vector
<
types
::
account_permission
>
{{
_create_account_name
,
"active"
}},
"newaccount"
,
types
::
newaccount
{
_create_account_name
,
new_account_name
,
owner_auth
,
active_auth
,
recovery_auth
,
deposit
});
trx
.
expiration
=
cc
.
head_block_time
()
+
fc
::
seconds
(
30
);
transaction_set_reference_block
(
trx
,
cc
.
head_block_id
());
trx
.
sign
(
_create_account_private_key
,
chainid
);
try
{
cc
.
push_transaction
(
trx
);
}
catch
(
const
account_name_exists_exception
&
)
{
// another transaction ended up adding the account, so look for alternates
return
find_alternates
(
new_account_name
);
}
_blocking_accounts
=
true
;
_timer
.
expires_from_now
(
boost
::
posix_time
::
microseconds
(
_create_interval_msec
*
1000
));
_timer
.
async_wait
(
boost
::
bind
(
&
faucet_testnet_plugin_impl
::
timer_fired
,
this
));
return
std
::
make_pair
(
account_created
,
fc
::
variant
(
eosio
::
detail
::
faucet_testnet_empty
()));
}
results_pair
create_faucet_account
(
const
std
::
string
&
body
)
{
const
eosio
::
detail
::
faucet_testnet_create_account_params
params
=
fc
::
json
::
from_string
(
body
).
as
<
eosio
::
detail
::
faucet_testnet_create_account_params
>
();
return
create_account
(
params
.
account
,
eosio
::
types
::
public_key
(
params
.
keys
.
owner
),
eosio
::
types
::
public_key
(
params
.
keys
.
active
));
}
const
chainbase
::
database
&
database
()
{
static
const
chainbase
::
database
*
db
=
nullptr
;
if
(
db
==
nullptr
)
db
=
&
_app
.
get_plugin
<
chain_plugin
>
().
chain
().
get_database
();
return
*
db
;
}
appbase
::
application
&
_app
;
boost
::
asio
::
deadline_timer
_timer
;
bool
_blocking_accounts
=
false
;
static
const
uint32_t
_default_create_interval_msec
;
uint32_t
_create_interval_msec
;
static
const
uint32_t
_default_create_alternates_time_limit_msec
;
uint32_t
_create_alternates_time_limit_msec
;
static
const
uint32_t
_default_create_alternates_to_return
;
uint32_t
_create_alternates_to_return
;
static
const
std
::
string
_default_create_account_name
;
types
::
account_name
_create_account_name
;
static
const
key_pair
_default_key_pair
;
fc
::
ecc
::
private_key
_create_account_private_key
;
public_key_type
_create_account_public_key
;
};
const
uint32_t
faucet_testnet_plugin_impl
::
_default_create_interval_msec
=
1000
;
const
uint32_t
faucet_testnet_plugin_impl
::
_default_create_alternates_time_limit_msec
=
5
;
const
uint32_t
faucet_testnet_plugin_impl
::
_default_create_alternates_to_return
=
3
;
const
std
::
string
faucet_testnet_plugin_impl
::
_default_create_account_name
=
"faucet"
;
const
key_pair
faucet_testnet_plugin_impl
::
_default_key_pair
=
{
"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"
,
"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"
};
faucet_testnet_plugin
::
faucet_testnet_plugin
()
:
my
(
new
faucet_testnet_plugin_impl
(
app
()))
{
}
faucet_testnet_plugin
::~
faucet_testnet_plugin
()
{}
void
faucet_testnet_plugin
::
set_program_options
(
options_description
&
,
options_description
&
cfg
)
{
cfg
.
add_options
()
(
"faucet-create-interval-ms"
,
bpo
::
value
<
uint32_t
>
()
->
default_value
(
faucet_testnet_plugin_impl
::
_default_create_interval_msec
),
"Time to wait, in milliseconds, between creating next faucet created account."
)
(
"faucet-alternates-time-limit-ms"
,
bpo
::
value
<
uint32_t
>
()
->
default_value
(
faucet_testnet_plugin_impl
::
_default_create_alternates_time_limit_msec
),
"Time to allow alternate account name suggestions before returning."
)
(
"faucet-alternates-to-return"
,
bpo
::
value
<
uint32_t
>
()
->
default_value
(
faucet_testnet_plugin_impl
::
_default_create_alternates_to_return
),
"Number of alternate account names to try to discover for already used account name."
)
(
"faucet-name"
,
bpo
::
value
<
std
::
string
>
()
->
default_value
(
faucet_testnet_plugin_impl
::
_default_create_account_name
),
"Name to use as creator for faucet created accounts."
)
(
"faucet-private-key"
,
boost
::
program_options
::
value
<
std
::
string
>
()
->
default_value
(
fc
::
json
::
to_string
(
faucet_testnet_plugin_impl
::
_default_key_pair
)),
"[public key, WIF private key] for signing for faucet creator account"
)
;
}
void
faucet_testnet_plugin
::
plugin_initialize
(
const
variables_map
&
options
)
{
my
->
_create_interval_msec
=
options
.
at
(
"faucet-create-interval-ms"
).
as
<
uint32_t
>
();
my
->
_create_alternates_time_limit_msec
=
options
.
at
(
"faucet-alternates-time-limit-ms"
).
as
<
uint32_t
>
();
my
->
_create_alternates_to_return
=
options
.
at
(
"faucet-alternates-to-return"
).
as
<
uint32_t
>
();
my
->
_create_account_name
=
options
.
at
(
"faucet-name"
).
as
<
std
::
string
>
();
auto
faucet_key_pair
=
fc
::
json
::
from_string
(
options
.
at
(
"faucet-private-key"
).
as
<
std
::
string
>
()).
as
<
key_pair
>
();
my
->
_create_account_public_key
=
public_key_type
(
faucet_key_pair
.
first
);
ilog
(
"Public Key: ${public}"
,
(
"public"
,
my
->
_create_account_public_key
));
fc
::
optional
<
fc
::
ecc
::
private_key
>
private_key
=
utilities
::
wif_to_key
(
faucet_key_pair
.
second
);
FC_ASSERT
(
private_key
,
"Invalid WIF-format private key ${key_string}"
,
(
"key_string"
,
faucet_key_pair
.
second
));
my
->
_create_account_private_key
=
std
::
move
(
*
private_key
);
}
void
faucet_testnet_plugin
::
plugin_startup
()
{
app
().
get_plugin
<
http_plugin
>
().
add_api
({
CALL
(
faucet
,
my
,
create_account
,
faucet_testnet_plugin_impl
::
create_faucet_account
)
});
}
void
faucet_testnet_plugin
::
plugin_shutdown
()
{
try
{
my
->
_timer
.
cancel
();
}
catch
(
fc
::
exception
&
e
)
{
edump
((
e
.
to_detail_string
()));
}
}
}
plugins/faucet_testnet_plugin/include/eosio/faucet_testnet_plugin/faucet_testnet_plugin.hpp
0 → 100644
浏览文件 @
e0a9a982
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#pragma once
#include <appbase/application.hpp>
#include <eos/http_plugin/http_plugin.hpp>
namespace
eosio
{
using
namespace
appbase
;
class
faucet_testnet_plugin
:
public
appbase
::
plugin
<
faucet_testnet_plugin
>
{
public:
faucet_testnet_plugin
();
~
faucet_testnet_plugin
();
APPBASE_PLUGIN_REQUIRES
((
http_plugin
))
virtual
void
set_program_options
(
options_description
&
,
options_description
&
cfg
)
override
;
void
plugin_initialize
(
const
variables_map
&
options
);
void
plugin_startup
();
void
plugin_shutdown
();
private:
std
::
unique_ptr
<
struct
faucet_testnet_plugin_impl
>
my
;
};
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录