先做笔记,后面整理更新
相同的需求如
https://eosio.stackexchange.com/questions/4012/double-sign-a-transaction-first-on-client-second-on-server?rq=1
想做的是执行action时做用户客户端以及服务端分别的账号权限验证。
推测逻辑如下
- 由客户通过scatter签名
- 发送到后端
- 由服务器签名
- 广播到网络
参考下 eosj
https://gist.github.com/adyliu/492503b94d0306371298f24e15481da4
{
"compression" : "none",
"transaction" : {
"expiration" : "2018-09-28T09:28:40.5",
"ref_block_num" : 16659166,
"ref_block_prefix" : 4253062493,
"max_net_usage_words" : 0,
"max_cpu_usage_ms" : 0,
"delay_sec" : 0,
"context_free_actions" : [ ],
"actions" : [ {
"account" : "eosio.token",
"name" : "transfer",
"authorization" : [ {
"actor" : "shijiebangmm",
"permission" : "active"
} ],
"data" : "20259be628f75cc3104208aee1a924e5290900000000000004454f53000000002f73656e7420627920656f732073646b202868747470733a2f2f6769746875622e636f6d2f6164796c69752f6a656f73"
} ],
"transaction_extensions" : [ ],
"context_free_data" : [ ]
},
"signatures" : [ "SIG_K1_KjY8Cs8zVdg4MNjGdJ51v252V6YVUVnhyDuDGjLuhfsuv4GQTnmu9uV59SiLMmNAAiaSmT7orb1iyZfXYHES15MFWRiBvy" ]
}
最后交易中 signatures 会有多个签名
https://github.com/adyliu/jeos/blob/53dbd027cd59d367d9a197cbff5a58bdd9bf7195/src/main/java/io/jafka/jeos/impl/LocalApiImpl.java
// ⑤ build the packed transaction
PackedTransaction packedTransaction = new PackedTransaction();
packedTransaction.setExpiration(arg.getHeadBlockTime().plusSeconds(arg.getExpiredSecond()));
packedTransaction.setRefBlockNum(arg.getLastIrreversibleBlockNum());
packedTransaction.setRefBlockPrefix(arg.getRefBlockPrefix());
packedTransaction.setMaxNetUsageWords(0);
packedTransaction.setMaxCpuUsageMs(0);
packedTransaction.setDelaySec(0);
packedTransaction.setActions(actions);
String hash = sign(privateKey, arg, packedTransaction);
PushTransactionRequest req = new PushTransactionRequest();
req.setTransaction(packedTransaction);
req.setSignatures(Arrays.asList(hash));
return req;
先解data,然后用服务端私钥签名后,将签名附加上去,
合约内增加对应的 require_auth
其他不必要的记录
auto res = ::check_transaction_authorization( prop.packed_transaction.data(), prop.packed_transaction.size(),
(const char*)0, 0,
packed_provided_approvals.data(), packed_provided_approvals.size()
);
扒扒链代码,找一下有没有相关的代码
transaction_id_type no_assert_id;
{
signed_transaction trx;
trx.actions.emplace_back( vector<permission_level>{{N(asserter),config::active_name}},
assertdef {1, "Should Not Assert!"} );
trx.actions[0].authorization = {{N(asserter),config::active_name}};
set_transaction_headers(trx);
trx.sign( get_private_key( N(asserter), "active" ), control->get_chain_id() );
auto result = push_transaction( trx );
BOOST_CHECK_EQUAL(result->receipt->status, transaction_receipt::executed);
BOOST_CHECK_EQUAL(result->action_traces.size(), 1u);
BOOST_CHECK_EQUAL(result->action_traces.at(0).receipt.receiver.to_string(), name(N(asserter)).to_string() );
BOOST_CHECK_EQUAL(result->action_traces.at(0).act.account.to_string(), name(N(asserter)).to_string() );
BOOST_CHECK_EQUAL(result->action_traces.at(0).act.name.to_string(), name(N(procassert)).to_string() );
BOOST_CHECK_EQUAL(result->action_traces.at(0).act.authorization.size(), 1u );
BOOST_CHECK_EQUAL(result->action_traces.at(0).act.authorization.at(0).actor.to_string(), name(N(asserter)).to_string() );
BOOST_CHECK_EQUAL(result->action_traces.at(0).act.authorization.at(0).permission.to_string(), name(config::active_name).to_string() );
no_assert_id = trx.id();
}
const signature_type& signed_transaction::sign(const private_key_type& key, const chain_id_type& chain_id) {
signatures.push_back(key.sign(sig_digest(chain_id, context_free_data)));
return signatures.back();
}
fc::microseconds
signed_transaction::get_signature_keys( const chain_id_type& chain_id, fc::time_point deadline,
flat_set<public_key_type>& recovered_pub_keys,
bool allow_duplicate_keys)const
{
return transaction::get_signature_keys(signatures, chain_id, deadline, context_free_data, recovered_pub_keys, allow_duplicate_keys);
}
看链代码,应该是支持一个交易多个签名的,那继续跟。
那看下eosjs
/** Sign a transaction */
public async sign({ chainId, requiredKeys, serializedTransaction }: SignatureProviderArgs) {
const signBuf = Buffer.concat([
new Buffer(chainId, 'hex'), new Buffer(serializedTransaction), new Buffer(new Uint8Array(32)),
]);
const signatures = requiredKeys.map(
(pub) => ecc.Signature.sign(signBuf, this.keys.get(convertLegacyPublicKey(pub))).toString(),
);
return { signatures, serializedTransaction };
}
https://github.com/EOSIO/eosjs/blob/849c03992e6ce3cb4b6a11bf18ab17b62136e5c9/src/eosjs-jssig.ts#L33
也是支持多个私钥给同一个交易签名的。
it('signs a transaction', async () => {
const eccSignatureSign = jest.spyOn(ecc.Signature, 'sign');
eccSignatureSign.mockImplementation((buffer, signKey) => signKey);
const provider = new JsSignatureProvider(privateKeys);
const chainId = '12345';
const requiredKeys = [
publicKeys[0],
publicKeys[2],
];
const serializedTransaction = new Uint8Array([
0, 16, 32, 128, 255,
]);
const abis: any[] = [];
const signOutput = await provider.sign({ chainId, requiredKeys, serializedTransaction, abis });
expect(eccSignatureSign).toHaveBeenCalledTimes(2);
expect(signOutput).toEqual({ signatures: [privateKeys[0], privateKeys[2]], serializedTransaction });
});
template <typename T>
transaction_trace_ptr CallAction(TESTER& test, T ac, const vector<account_name>& scope = {N(testapi)}) {
signed_transaction trx;
auto pl = vector<permission_level>{{scope[0], config::active_name}};
if (scope.size() > 1)
for (size_t i = 1; i < scope.size(); i++)
pl.push_back({scope[i], config::active_name});
action act(pl, ac);
trx.actions.push_back(act);
test.set_transaction_headers(trx);
auto sigs = trx.sign(test.get_private_key(scope[0], "active"), test.control->get_chain_id());
flat_set<public_key_type> keys;
trx.get_signature_keys(test.control->get_chain_id(), fc::time_point::maximum(), keys);
auto res = test.push_transaction(trx);
BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed);
test.produce_block();
return res;
}