您正在查看: EOS-新手问答 分类下的文章

eos linkauth unlinkauth

https://github.com/EOSIO/eos/blob/3fddb727b8f3615917707281dfd3dd3cc5d3d66d/libraries/chain/eosio_contract.cpp#L301

void apply_eosio_linkauth(apply_context& context) {
//   context.require_write_lock( config::eosio_auth_scope );

   auto requirement = context.act.data_as<linkauth>();
   try {
      EOS_ASSERT(!requirement.requirement.empty(), action_validate_exception, "Required permission cannot be empty");

      context.require_authorization(requirement.account); // only here to mark the single authority on this action as used

      auto& db = context.db;
      const auto *account = db.find<account_object, by_name>(requirement.account);
      EOS_ASSERT(account != nullptr, account_query_exception,
                 "Failed to retrieve account: ${account}", ("account", requirement.account)); // Redundant?
      const auto *code = db.find<account_object, by_name>(requirement.code);
      EOS_ASSERT(code != nullptr, account_query_exception,
                 "Failed to retrieve code for account: ${account}", ("account", requirement.code));
      if( requirement.requirement != config::eosio_any_name ) {
         const auto *permission = db.find<permission_object, by_name>(requirement.requirement);
         EOS_ASSERT(permission != nullptr, permission_query_exception,
                    "Failed to retrieve permission: ${permission}", ("permission", requirement.requirement));
      }

      auto link_key = boost::make_tuple(requirement.account, requirement.code, requirement.type);
      auto link = db.find<permission_link_object, by_action_name>(link_key);

      if( link ) {
         EOS_ASSERT(link->required_permission != requirement.requirement, action_validate_exception,
                    "Attempting to update required authority, but new requirement is same as old");
         db.modify(*link, [requirement = requirement.requirement](permission_link_object& link) {
             link.required_permission = requirement;
         });
      } else {
         const auto& l =  db.create<permission_link_object>([&requirement](permission_link_object& link) {
            link.account = requirement.account;
            link.code = requirement.code;
            link.message_type = requirement.type;
            link.required_permission = requirement.requirement;
         });

         context.add_ram_usage(
            l.account,
            (int64_t)(config::billable_size_v<permission_link_object>)
         );
      }

  } FC_CAPTURE_AND_RETHROW((requirement))
}

void apply_eosio_unlinkauth(apply_context& context) {
//   context.require_write_lock( config::eosio_auth_scope );

   auto& db = context.db;
   auto unlink = context.act.data_as<unlinkauth>();

   context.require_authorization(unlink.account); // only here to mark the single authority on this action as used

   auto link_key = boost::make_tuple(unlink.account, unlink.code, unlink.type);
   auto link = db.find<permission_link_object, by_action_name>(link_key);
   EOS_ASSERT(link != nullptr, action_validate_exception, "Attempting to unlink authority, but no link found");
   context.add_ram_usage(
      link->account,
      -(int64_t)(config::billable_size_v<permission_link_object>)
   );

   db.remove(*link);
}
auto link_key = boost::make_tuple(requirement.account, requirement.code, requirement.type);
auto link = db.find<permission_link_object, by_action_name>(link_key);

父级可以把自己已有的action执行权限link给子级,同一子级只能有一个权限有同一action执行权限,赋值给同一级其他的,会把原先的删除掉。如果子级有某个action执行权限,那父级也有。如果子级unlink了某一action执行权限,将从此级追溯到顶父级active或者owner,顶父级之前的此action执行权限都会删除掉(因为active和owner为特殊账户,不会被删除)。
因为account,code,type三个值加起来是查询索引,所以只有一条记录,记录着当前action执行权限link到哪个子权限了,并且子级有的话,父级就有。
新创建的权限,没link的话,没有任何action执行权限。

一个交易,多个签名的调研

先做笔记,后面整理更新
相同的需求如
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();
   }

https://github.com/EOSIO/eos/blob/686f0deb5dac097cc292f735ccb47c238e763de0/unittests/wasm_tests.cpp#L85

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();
}

https://github.com/EOSIO/eos/blob/686f0deb5dac097cc292f735ccb47c238e763de0/libraries/chain/transaction.cpp#L138

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);
}

https://github.com/EOSIO/eos/blob/686f0deb5dac097cc292f735ccb47c238e763de0/libraries/chain/transaction.cpp#L147

看链代码,应该是支持一个交易多个签名的,那继续跟。

那看下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 });
    });

https://github.com/EOSIO/eosjs/blob/849c03992e6ce3cb4b6a11bf18ab17b62136e5c9/src/tests/eosjs-jssig.test.ts#L37

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;
}

https://github.com/EOSIO/eos/blob/686f0deb5dac097cc292f735ccb47c238e763de0/unittests/api_tests.cpp#L198

eos block_status

https://github.com/EOSIO/eos/blob/686f0deb5dac097cc292f735ccb47c238e763de0/libraries/chain/include/eosio/chain/controller.hpp#L87

enum class block_status {
            irreversible = 0, ///< this block has already been applied before by this node and is considered irreversible
            validated   = 1, ///< this is a complete block signed by a valid producer and has been previously applied by this node and therefore validated but it is not yet irreversible
            complete   = 2, ///< this is a complete block signed by a valid producer but is not yet irreversible nor has it yet been applied by this node
            incomplete  = 3, ///< this is an incomplete block (either being produced by a producer or speculatively produced by a node)
         };

EOS 代币命名规则

constexpr explicit symbol_code( std::string_view str )
      :value(0)
      {
         if( str.size() > 7 ) {
            eosio::check( false, "string is too long to be a valid symbol_code" );
         }
         for( auto itr = str.rbegin(); itr != str.rend(); ++itr ) {
            if( *itr < 'A' || *itr > 'Z') {
               eosio::check( false, "only uppercase letters allowed in symbol_code string" );
            }
            value <<= 8;
            value |= *itr;
         }
      }

格式

<=7位,A-Z