Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
YottaChain
YTBP
提交
b9f27e15
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,发现更多精彩内容 >>
提交
b9f27e15
编写于
5月 09, 2018
作者:
A
arhag
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'slim' into slim-fix-permissions
上级
d86d724f
08666f9e
变更
11
隐藏空白更改
内联
并排
Showing
11 changed file
with
176 addition
and
164 deletion
+176
-164
contracts/test_api/test_api.cpp
contracts/test_api/test_api.cpp
+1
-1
contracts/test_api/test_api.hpp
contracts/test_api/test_api.hpp
+1
-1
contracts/test_api/test_api_common.hpp
contracts/test_api/test_api_common.hpp
+19
-0
contracts/test_api/test_transaction.cpp
contracts/test_api/test_transaction.cpp
+12
-7
libraries/chain/abi_serializer.cpp
libraries/chain/abi_serializer.cpp
+1
-0
tests/CMakeLists.txt
tests/CMakeLists.txt
+1
-1
tests/nodeos_run_remote_test.py
tests/nodeos_run_remote_test.py
+9
-3
tests/testUtils.py
tests/testUtils.py
+3
-1
unittests/CMakeLists.txt
unittests/CMakeLists.txt
+0
-2
unittests/abi_tests.cpp
unittests/abi_tests.cpp
+83
-141
unittests/api_tests.cpp
unittests/api_tests.cpp
+46
-7
未找到文件。
contracts/test_api/test_api.cpp
浏览文件 @
b9f27e15
...
...
@@ -135,7 +135,7 @@ extern "C" {
WASM_TEST_HANDLER_EX
(
test_transaction
,
send_action_sender
);
WASM_TEST_HANDLER
(
test_transaction
,
deferred_print
);
WASM_TEST_HANDLER_EX
(
test_transaction
,
send_deferred_transaction
);
WASM_TEST_HANDLER
(
test_transaction
,
send_deferred_tx_
given_payer
);
WASM_TEST_HANDLER
(
test_transaction
,
send_deferred_tx_
with_dtt_action
);
WASM_TEST_HANDLER
(
test_transaction
,
cancel_deferred_transaction
);
WASM_TEST_HANDLER
(
test_transaction
,
send_cf_action
);
WASM_TEST_HANDLER
(
test_transaction
,
send_cf_action_fail
);
...
...
contracts/test_api/test_api.hpp
浏览文件 @
b9f27e15
...
...
@@ -165,7 +165,7 @@ struct test_transaction {
static
void
send_action_sender
(
uint64_t
receiver
,
uint64_t
code
,
uint64_t
action
);
static
void
deferred_print
();
static
void
send_deferred_transaction
(
uint64_t
receiver
,
uint64_t
code
,
uint64_t
action
);
static
void
send_deferred_tx_
given_payer
();
static
void
send_deferred_tx_
with_dtt_action
();
static
void
cancel_deferred_transaction
();
static
void
send_cf_action
();
static
void
send_cf_action_fail
();
...
...
contracts/test_api/test_api_common.hpp
浏览文件 @
b9f27e15
...
...
@@ -58,6 +58,25 @@ struct cf_action {
EOSLIB_SERIALIZE
(
cf_action
,
(
payload
)(
cfd_idx
)
)
};
// Deferred Transaction Trigger Action
struct
dtt_action
{
static
uint64_t
get_name
()
{
return
WASM_TEST_ACTION
(
"test_transaction"
,
"send_deferred_tx_with_dtt_action"
);
}
static
uint64_t
get_account
()
{
return
N
(
testapi
);
}
uint64_t
payer
=
N
(
testapi
);
uint64_t
deferred_account
=
N
(
testapi
);
uint64_t
deferred_action
=
WASM_TEST_ACTION
(
"test_transaction"
,
"deferred_print"
);
uint64_t
permission_name
=
N
(
active
);
uint32_t
delay_sec
=
2
;
EOSLIB_SERIALIZE
(
dtt_action
,
(
payer
)(
deferred_account
)(
deferred_action
)(
permission_name
)(
delay_sec
)
)
};
#pragma pack(pop)
static_assert
(
sizeof
(
dummy_action
)
==
13
,
"unexpected packing"
);
...
...
contracts/test_api/test_transaction.cpp
浏览文件 @
b9f27e15
...
...
@@ -242,18 +242,23 @@ void test_transaction::send_deferred_transaction(uint64_t receiver, uint64_t, ui
trx
.
send
(
0xffffffffffffffff
,
receiver
);
}
void
test_transaction
::
send_deferred_tx_
given_payer
()
{
void
test_transaction
::
send_deferred_tx_
with_dtt_action
()
{
using
namespace
eosio
;
uint64_t
payer
;
read_action_data
(
&
payer
,
action_data_size
());
dtt_action
dtt_act
;
read_action_data
(
&
dtt_act
,
action_data_size
());
action
deferred_act
;
deferred_act
.
account
=
dtt_act
.
deferred_account
;
deferred_act
.
name
=
dtt_act
.
deferred_action
;
deferred_act
.
authorization
=
vector
<
permission_level
>
{{
N
(
testapi
),
dtt_act
.
permission_name
}};
auto
trx
=
transaction
();
test_action_action
<
N
(
testapi
),
WASM_TEST_ACTION
(
"test_transaction"
,
"deferred_print"
)
>
test_action
;
trx
.
actions
.
emplace_back
(
vector
<
permission_level
>
{{
N
(
testapi
),
N
(
active
)}},
test_action
);
trx
.
delay_sec
=
2
;
trx
.
send
(
0xffffffffffffffff
,
payer
);
trx
.
actions
.
emplace_back
(
deferred_act
);
trx
.
delay_sec
=
dtt_act
.
delay_sec
;
trx
.
send
(
0xffffffffffffffff
,
dtt_act
.
payer
);
}
void
test_transaction
::
cancel_deferred_transaction
()
{
using
namespace
eosio
;
cancel_deferred
(
0xffffffffffffffff
);
//use the same id (0) as in send_deferred_transaction
...
...
libraries/chain/abi_serializer.cpp
浏览文件 @
b9f27e15
...
...
@@ -63,6 +63,7 @@ namespace eosio { namespace chain {
//asset.hpp
built_in_types
.
emplace
(
"asset"
,
pack_unpack
<
asset
>
());
built_in_types
.
emplace
(
"extended_asset"
,
pack_unpack
<
extended_asset
>
());
//native.hpp
built_in_types
.
emplace
(
"string"
,
pack_unpack
<
string
>
());
...
...
tests/CMakeLists.txt
浏览文件 @
b9f27e15
...
...
@@ -39,7 +39,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/consensus-validation-malicious-produc
#To run plugin_test with all log from blockchain displayed, put --verbose after --, i.e. plugin_test -- --verbose
add_test
(
NAME plugin_test COMMAND plugin_test --report_level=detailed --color_output
)
add_test
(
NAME nodeos_run_test COMMAND tests/nodeos_run_test.py -v --only-bios --dump-error-detail WORKING_DIRECTORY
${
CMAKE_BINARY_DIR
}
)
# TODO removed on slim: add_test(NAME nodeos_run_remote_test COMMAND tests/nodeos_run_remote_test.py -v
--dump-error-detail WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_test
(
NAME nodeos_run_remote_test COMMAND tests/nodeos_run_remote_test.py -v --only-bios
--dump-error-detail WORKING_DIRECTORY
${
CMAKE_BINARY_DIR
}
)
# TODO removed on slim: add_test(NAME p2p_dawn515_test COMMAND tests/p2p_tests/dawn_515/test.sh WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
#if(BUILD_MONGO_DB_PLUGIN)
# add_test(NAME nodeos_run_test-mongodb COMMAND tests/nodeos_run_test.py --mongodb -v --dump-error-detail WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
...
...
tests/nodeos_run_remote_test.py
浏览文件 @
b9f27e15
...
...
@@ -14,6 +14,7 @@ def errorExit(msg="", errorCode=1):
parser
=
argparse
.
ArgumentParser
()
parser
.
add_argument
(
"-v"
,
help
=
"verbose"
,
action
=
'store_true'
)
parser
.
add_argument
(
"--dont-kill"
,
help
=
"Leave cluster running after test finishes"
,
action
=
'store_true'
)
parser
.
add_argument
(
"--only-bios"
,
help
=
"Limit testing to bios node."
,
action
=
'store_true'
)
parser
.
add_argument
(
"--dump-error-details"
,
help
=
"Upon error print etc/eosio/node_*/config.ini and var/lib/node_*/stderr.log to stdout"
,
action
=
'store_true'
)
...
...
@@ -22,6 +23,7 @@ args = parser.parse_args()
debug
=
args
.
v
dontKill
=
args
.
dont_kill
dumpErrorDetails
=
args
.
dump_error_details
onlyBios
=
args
.
only_bios
testUtils
.
Utils
.
Debug
=
debug
...
...
@@ -43,7 +45,7 @@ try:
Print
(
"producing nodes: %s, non-producing nodes: %d, topology: %s, delay between nodes launch(seconds): %d"
%
(
pnodes
,
total_nodes
-
pnodes
,
topo
,
delay
))
Print
(
"Stand up cluster"
)
if
cluster
.
launch
(
pnodes
,
total_nodes
,
prodCount
,
topo
,
delay
)
is
False
:
if
cluster
.
launch
(
pnodes
,
total_nodes
,
prodCount
,
topo
,
delay
,
onlyBios
=
onlyBios
,
dontKill
=
dontKill
)
is
False
:
errorExit
(
"Failed to stand up eos cluster."
)
Print
(
"Wait for Cluster stabilization"
)
...
...
@@ -55,15 +57,19 @@ try:
initaPrvtKey
=
producerKeys
[
"inita"
][
"private"
]
initbPrvtKey
=
producerKeys
[
"initb"
][
"private"
]
cmd
=
"%s --dont-launch --inita_prvt_key %s --initb_prvt_key %s %s %s
"
%
(
actualTest
,
initaPrvtKey
,
initbPrvtKey
,
"-v"
if
debug
else
""
,
"--dont-kill"
if
dontKill
else
""
)
cmd
=
"%s --dont-launch --inita_prvt_key %s --initb_prvt_key %s %s %s
%s"
%
(
actualTest
,
initaPrvtKey
,
initbPrvtKey
,
"-v"
if
debug
else
""
,
"--dont-kill"
if
dontKill
else
""
,
"--only-bios"
if
onlyBios
else
""
)
Print
(
"Starting up %s test: %s"
%
(
"nodeos"
,
actualTest
))
Print
(
"cmd: %s
\n
"
%
(
cmd
))
if
0
!=
subprocess
.
call
(
cmd
,
shell
=
True
):
errorExit
(
"failed to run cmd."
)
testSuccessful
=
True
Print
(
"
\n
END"
)
finally
:
if
testSuccessful
:
Print
(
"Test succeeded."
)
else
:
Print
(
"Test failed."
)
if
not
testSuccessful
and
dumpErrorDetails
:
cluster
.
dumpErrorDetails
()
Print
(
"== Errors see above =="
)
...
...
tests/testUtils.py
浏览文件 @
b9f27e15
...
...
@@ -1303,7 +1303,9 @@ class Cluster(object):
return
True
# Initialize the default nodes (at present just the root node)
def
initializeNodes
(
self
,
initaPrvtKey
=
None
,
initbPrvtKey
=
None
):
def
initializeNodes
(
self
,
initaPrvtKey
=
None
,
initbPrvtKey
=
None
,
onlyBios
=
False
):
port
=
Cluster
.
__BiosPort
if
onlyBios
else
self
.
port
host
=
Cluster
.
__BiosHost
if
onlyBios
else
self
.
host
node
=
Node
(
self
.
host
,
self
.
port
,
enableMongo
=
self
.
enableMongo
,
mongoHost
=
self
.
mongoHost
,
mongoPort
=
self
.
mongoPort
,
mongoDb
=
self
.
mongoDb
)
node
.
setWalletEndpointArgs
(
self
.
walletEndpointArgs
)
if
Utils
.
Debug
:
Utils
.
Print
(
"Node:"
,
node
)
...
...
unittests/CMakeLists.txt
浏览文件 @
b9f27e15
...
...
@@ -34,12 +34,10 @@ add_dependencies(unit_test asserter test_api test_api_mem test_api_db test_api_m
add_test
(
NAME unit_test_binaryen COMMAND unit_test
-t \!eosio_system_tests/*
-t \!currency_tests/test_deferred_failure
-t \!abi_tests/abigen_all_types
--report_level=detailed --color_output -- --binaryen
)
add_test
(
NAME unit_test_wavm COMMAND unit_test
-t \!eosio_system_tests/*
-t \!currency_tests/test_deferred_failure
-t \!abi_tests/abigen_all_types
--report_level=detailed --color_output --catch_system_errors=no -- --wavm
)
if
(
ENABLE_COVERAGE_TESTING
)
...
...
unittests/abi_tests.cpp
浏览文件 @
b9f27e15
...
...
@@ -460,203 +460,145 @@ BOOST_FIXTURE_TEST_CASE(abigen_all_types, abi_gen_helper)
{
try
{
const
char
*
all_types
=
R"=====(
#include <eosiolib/types.hpp>
#include <eosiolib/asset.hpp>
#include <string>
typedef int field;
typedef int struct_def;
typedef int fields;
typedef int permission_level;
typedef int action;
typedef int permission_level_weight;
typedef int transaction;
typedef int signed_transaction;
typedef int key_weight;
typedef int authority;
typedef int chain_config;
typedef int type_def;
typedef int action_def;
typedef int table_def;
typedef int abi_def;
typedef int nonce;
#pragma GCC diagnostic ignored "-Wpointer-bool-conversion"
#include <eosiolib/types.hpp>
#include <eosiolib/asset.hpp>
#include <eosiolib/action.hpp>
#include <string>
typedef eosio::symbol_type symbol;
//@abi action
struct test_struct {
std::string field1;
time field2;
signature field3;
checksum256 field4;
field_name field5;
fixed_string32 field6;
fixed_string16 field7;
type_name field8;
uint8_t field9;
uint16_t field10;
uint32_t field11;
uint64_t field12;
uint128_t field13;
//uint256 field14;
int8_t field15;
int16_t field16;
int32_t field17;
int64_t field18;
eosio::name field19;
field field20;
struct_def field21;
fields field22;
account_name field23;
permission_name field24;
action_name field25;
scope_name field26;
permission_level field27;
action field28;
permission_level_weight field29;
transaction field30;
signed_transaction field31;
key_weight field32;
authority field33;
chain_config field34;
type_def field35;
action_def field36;
table_def field37;
abi_def field38;
public_key field39;
eosio::asset field40;
std::string field1;
time field2;
signature field3;
checksum256 field4;
field_name field5;
fixed_string32 field6;
fixed_string16 field7;
type_name field8;
uint8_t field9;
uint16_t field10;
uint32_t field11;
uint64_t field12;
uint128_t field13;
int8_t field15;
int16_t field16;
int32_t field17;
int64_t field18;
eosio::name field19;
account_name field23;
permission_name field24;
action_name field25;
scope_name field26;
eosio::permission_level field27;
public_key field39;
eosio::asset field40;
eosio::extended_asset field41;
symbol field42;
};
)====="
;
const
char
*
all_types_abi
=
R"=====(
{
"types": [],
"structs": [{
"name"
: "test_struct",
"base"
: "",
"fields"
: [{
"name": "field1",
"type": "string"
},{
"types": [],
"structs": [{
"name"
: "test_struct",
"base"
: "",
"fields"
: [{
"name": "field1",
"type": "string"
},{
"name": "field2",
"type": "time"
},{
},{
"name": "field3",
"type": "signature"
},{
},{
"name": "field4",
"type": "checksum256"
},{
},{
"name": "field5",
"type": "field_name"
},{
},{
"name": "field6",
"type": "fixed_string32"
},{
},{
"name": "field7",
"type": "fixed_string16"
},{
},{
"name": "field8",
"type": "type_name"
},{
},{
"name": "field9",
"type": "uint8"
},{
},{
"name": "field10",
"type": "uint16"
},{
},{
"name": "field11",
"type": "uint32"
},{
},{
"name": "field12",
"type": "uint64"
},{
},{
"name": "field13",
"type": "uint128"
},{
},{
"name": "field15",
"type": "int8"
},{
},{
"name": "field16",
"type": "int16"
},{
},{
"name": "field17",
"type": "int32"
},{
},{
"name": "field18",
"type": "int64"
},{
},{
"name": "field19",
"type": "name"
},{
"name": "field20",
"type": "field"
},{
"name": "field21",
"type": "struct_def"
},{
"name": "field22",
"type": "fields"
},{
},{
"name": "field23",
"type": "account_name"
},{
},{
"name": "field24",
"type": "permission_name"
},{
},{
"name": "field25",
"type": "action_name"
},{
},{
"name": "field26",
"type": "scope_name"
},{
},{
"name": "field27",
"type": "permission_level"
},{
"name": "field28",
"type": "action"
},{
"name": "field29",
"type": "permission_level_weight"
},{
"name": "field30",
"type": "transaction"
},{
"name": "field31",
"type": "signed_transaction"
},{
"name": "field32",
"type": "key_weight"
},{
"name": "field33",
"type": "authority"
},{
"name": "field34",
"type": "chain_config"
},{
"name": "field35",
"type": "type_def"
},{
"name": "field36",
"type": "action_def"
},{
"name": "field37",
"type": "table_def"
},{
"name": "field38",
"type": "abi_def"
},{
},{
"name": "field39",
"type": "public_key"
},{
},{
"name": "field40",
"type": "asset"
}]
}],
"actions": [{
"name" : "teststruct",
"type" : "test_struct"
}],
"tables": [],
"ricardian_clauses": []
},{
"name": "field41",
"type": "extended_asset"
},{
"name": "field42",
"type": "symbol"
}
]
}
],
"actions": [{
"name": "teststruct",
"type": "test_struct",
"ricardian_contract": ""
}
],
"tables": [],
"ricardian_clauses": []
}
)====="
;
BOOST_TEST
(
generate_abi
(
all_types
,
all_types_abi
)
==
true
);
...
...
unittests/api_tests.cpp
浏览文件 @
b9f27e15
...
...
@@ -49,6 +49,7 @@
FC_REFLECT
(
dummy_action
,
(
a
)(
b
)(
c
)
)
FC_REFLECT
(
u128_action
,
(
values
)
)
FC_REFLECT
(
cf_action
,
(
payload
)(
cfd_idx
)
)
FC_REFLECT
(
dtt_action
,
(
payer
)(
deferred_account
)(
deferred_action
)(
permission_name
)(
delay_sec
)
)
FC_REFLECT
(
invalid_access_action
,
(
code
)(
val
)(
index
)(
store
)
)
#ifdef NON_VALIDATING_TEST
...
...
@@ -894,15 +895,53 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try {
BOOST_CHECK_THROW(CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_transaction", {}), transaction_exception);
#endif
// Send deferred transaction with payer != receiver, payer is alice in this case, this should fail since we don't have authorization of alice
BOOST_CHECK_THROW
(
CALL_TEST_FUNCTION
(
*
this
,
"test_transaction"
,
"send_deferred_tx_given_payer"
,
fc
::
raw
::
pack
(
account_name
(
"alice"
))),
missing_auth_exception
);
// If we make testapi to be priviledge account, deferred transaction will work no matter who is the payer
{
// Trigger a tx which in turn sends a deferred tx with payer != receiver
// Payer is alice in this case, this tx should fail since we don't have the authorization of alice
dtt_action
dtt_act1
;
dtt_act1
.
payer
=
N
(
alice
);
BOOST_CHECK_THROW
(
CALL_TEST_FUNCTION
(
*
this
,
"test_transaction"
,
"send_deferred_tx_with_dtt_action"
,
fc
::
raw
::
pack
(
dtt_act1
)),
missing_auth_exception
);
// Send a tx which in turn sends a deferred tx with the deferred tx's receiver != this tx receiver
// This will include the authorization of the receiver, and impose any related delay associated with the authority
// We set the authorization delay to be 10 sec here, and since the deferred tx delay is set to be 5 sec, so this tx should fail
dtt_action
dtt_act2
;
dtt_act2
.
deferred_account
=
N
(
testapi2
);
dtt_act2
.
permission_name
=
N
(
additional
);
dtt_act2
.
delay_sec
=
5
;
push_action
(
config
::
system_account_name
,
updateauth
::
get_name
(),
"testapi"
,
fc
::
mutable_variant_object
()
(
"account"
,
"testapi"
)
(
"permission"
,
name
(
dtt_act2
.
permission_name
))
(
"parent"
,
"active"
)
(
"auth"
,
authority
(
get_public_key
(
"testapi"
,
name
(
dtt_act2
.
permission_name
).
to_string
()),
10
)));
push_action
(
config
::
system_account_name
,
linkauth
::
get_name
(),
"testapi"
,
fc
::
mutable_variant_object
()
(
"account"
,
"testapi"
)
(
"code"
,
name
(
dtt_act2
.
deferred_account
))
(
"type"
,
name
(
dtt_act2
.
deferred_action
))
(
"requirement"
,
name
(
dtt_act2
.
permission_name
)));
BOOST_CHECK_THROW
(
CALL_TEST_FUNCTION
(
*
this
,
"test_transaction"
,
"send_deferred_tx_with_dtt_action"
,
fc
::
raw
::
pack
(
dtt_act2
)),
insufficient_delay_exception
);
// Meanwhile, if the deferred tx receiver == this tx receiver, the delay will be ignored, this tx should succeed
dtt_action
dtt_act3
;
dtt_act3
.
deferred_account
=
N
(
testapi
);
dtt_act3
.
permission_name
=
N
(
additional
);
push_action
(
config
::
system_account_name
,
linkauth
::
get_name
(),
"testapi"
,
fc
::
mutable_variant_object
()
(
"account"
,
"testapi"
)
(
"code"
,
name
(
dtt_act3
.
deferred_account
))
(
"type"
,
name
(
dtt_act3
.
deferred_action
))
(
"requirement"
,
name
(
dtt_act3
.
permission_name
)));
CALL_TEST_FUNCTION
(
*
this
,
"test_transaction"
,
"send_deferred_tx_with_dtt_action"
,
fc
::
raw
::
pack
(
dtt_act3
));
// If we make testapi account to be priviledged account:
// - the deferred transaction will work no matter who is the payer
// - the deferred transaction will not care about the delay of the authorization
push_action
(
config
::
system_account_name
,
N
(
setpriv
),
config
::
system_account_name
,
mutable_variant_object
()
(
"account"
,
"testapi"
)
(
"is_priv"
,
1
));
CALL_TEST_FUNCTION
(
*
this
,
"test_transaction"
,
"send_deferred_transaction"
,
fc
::
raw
::
pack
(
account_name
(
"alice"
)));
(
"is_priv"
,
1
));
CALL_TEST_FUNCTION
(
*
this
,
"test_transaction"
,
"send_deferred_tx_with_dtt_action"
,
fc
::
raw
::
pack
(
dtt_act1
));
CALL_TEST_FUNCTION
(
*
this
,
"test_transaction"
,
"send_deferred_tx_with_dtt_action"
,
fc
::
raw
::
pack
(
dtt_act2
));
}
BOOST_REQUIRE_EQUAL
(
validate
(),
true
);
}
FC_LOG_AND_RETHROW
()
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录