EOS签名算法分析

该博客详细介绍了EOS交易签名的过程,包括使用eospy库创建交易、序列化交易、计算交易哈希以及使用ECDSA进行签名。还提供了一个自定义的EOsKey类用于调试,展示了如何通过私钥生成签名并广播交易。
摘要由CSDN通过智能技术生成
def sig_digest(payload, chain_id=None, context_free_data=None) :
    ''' '''
    if chain_id :
        buf = bytearray.fromhex(chain_id)
    else :
        buf = bytearray(32)
    # already a bytearray
    buf.extend(payload)
    if context_free_data :
        #buf += sha256(context_free_data)
        pass
    else :
        # empty buffer
        buf.extend(bytearray(32))
    return sha256(buf)


def eos_make_raw_transaction():
    ce = eospy.cleos.Cleos(url='http://jungle2.cryptolions.io:80')

    arguments = {
        "from": "yangqingqin1",    #发送账户
        "to": "yangqingqing",      #接收账户
        "quantity": '0.1234 EOS',  #数量
        "memo": "EOS to the moon", #备注
    }
    payload = {
        "account": "eosio.token",  #合约账户
        "name": "transfer",        #方法名
        "authorization": [{        #权限
            "actor": "yangqingqin1",
            "permission": "active",
        }],
    }

    # Converting payload to binary
    data = ce.abi_json_to_bin(payload['account'], payload['name'], arguments)
    # Inserting payload binary form as "data" field in original payload
    # rsp:{'binargs': '10a6b36c3acba6f1c0a6b36c3acba6f1d20400000000000004454f53000000000f454f5320746f20746865206d6f6f6e'}
    payload['data'] = data['binargs']
    
    # final transaction formed
    trx = {"actions": [payload]}
    
    #交易过期时间, 如果交易在此时间前未被打包, 则视为过期交易
    #最大过期时间可设置1小时
    import datetime as dt
    trx['expiration'] = str((dt.datetime.utcnow() + dt.timedelta(seconds=60 * 60)).replace(tzinfo=pytz.UTC))

    print(json.dumps(trx))

    #eospy库的源码是将签名和广播都放在了  push_transaction 中, 
    #这里将交易签名和交易广播两个功能独立出来
    # key = eospy.keys.EOSKey('5JfUC7k6yGs5RCoHeX464TqZnPWgdqrFfsETBzYGPB9ipDfNyzw')
    # resp = ce.push_transaction(trx, key, broadcast=False)
    # print(resp)

    chain_info, lib_info = ce.get_chain_lib_info()
    rawtx = Transaction(trx, chain_info, lib_info)  #广播json的数据
    encoded = rawtx.encode()
    print( "encode: {}".format( encoded) )
    
    #以下是对交易进行序列化(小端), 具体实现见 Transaction.encode()的源码
    # digest 是交易序列化后的sha256哈希值 , digest 也是后面进行ECC签名的输入参数
    # 所谓的交易签名就是digest进行签名
    from eospy.utils import sig_digest
    digest = sig_digest(rawtx.encode(), chain_info['chain_id'])
    print( "digest: {}".format( digest) )

    return rawtx, digest
  • EOS交易签名的核心代码

    import base58
    import os
    import ecdsa
    import re
    from binascii import hexlify, unhexlify
    import hashlib
    import time
    import struct
    import array
    
    
    
    #重写EOsKey 方便调试
    class MyEosKey( eospy.keys.EOSKey ):
    
        def __init__(self, private_str='') :
          eospy.keys.EOSKey.__init__(self, private_str)
        
        def _check_encode(self, key_buffer, key_type=None) :
            '''    '''
            if isinstance(key_buffer, bytes) :
                key_buffer = key_buffer.decode()
            check = key_buffer
            if key_type == 'sha256x2' :
                first_sha = sha256(unhexlify(check))
                chksum = sha256(unhexlify(first_sha))[:8]   #取前4字节作为校验和
            else :
                if key_type :
                    check += hexlify(bytearray(key_type,'utf-8')).decode()  #将"K1"追加到末尾
                chksum = ripemd160(unhexlify(check))[:8]
            return base58.b58encode(unhexlify(key_buffer+chksum))
    
        def sign_ex(self, digest):
            ''' '''
            cnt = 0
            # convert digest to hex string
            digest = unhexlify(digest)
            if len(digest) != 32:
                raise ValueError("32 byte buffer required")
            while 1:
                
                # get deterministic k
                if cnt:
                    sha_digest = hashlib.sha256(digest + bytearray(cnt)).digest()
                else:
                    sha_digest = hashlib.sha256(digest).digest()
    
                print("sha:{}".format(  hexlify(sha_digest)))
    
                k = ecdsa.rfc6979.generate_k(self._sk.curve.generator.order(),
                                             self._sk.privkey.secret_multiplier,
                                             hashlib.sha256,
                                               # hashlib.sha256(digest + struct.pack('d', time.time())).digest() # use time to randomize
                                             sha_digest
                                             )
                
                # sign the message
                sigder = self._sk.sign_digest(digest, sigencode=ecdsa.util.sigencode_der, k=k)
                
                print('sigder:' + ''.join( [hex(i)[2:]  for i in sigder ]))
    
                # reformat sig
                r, s = ecdsa.util.sigdecode_der(sigder, self._sk.curve.generator.order())
                print("r:{}".format(  hex(r)))
                print("s:{}".format(  hex(s)))
    
                sigder = array.array('B', sigder)
    
                sig = ecdsa.util.sigencode_string(r, s, self._sk.curve.generator.order())
    
                # ensure signature is canonical
                lenR = sigder[3]
                lenS = sigder[5 + lenR]
    
                if lenR == 32 and lenS == 32:
                    # derive recover parameter
                    i = self._recovery_pubkey_param(digest, sig)
                    # compact
                    i += 27
                    # compressed
                    i += 4
                    sigstr = struct.pack('<B', i) + sig
                    print([ i for i in sigstr])
                    break
                cnt += 1
                if not cnt % 10:
                    print('Still searching for a signature. Tried {} times.'.format(cnt))
                    
    
            print( "sigstr:{}".format( hexlify(sigstr)) )
            
            #EOS支持 "secp256k1" 和 "secp256r1"  两个版本的ECC签名
            #这里只讨论  "k1" 版本
            return 'SIG_K1_' + self._check_encode(hexlify(sigstr), 'K1').decode()
    
    
    def eos_sign_raw_transaction(digest):
        key = MyEosKey('5JfUC7k6yGs5RCoHeX464TqZnPWgdqrFfsETBzYGPB9ipDfNyzw')
        return [ key.sign_ex(digest) ]
        
        
    trx,digest = eos_make_raw_transaction()
    print("digest:{}".format(digest))
    sig = eos_sign_raw_transaction(digest)
    
    #生成广播的json数据
    final_trx = {
            'compression': 'none',
            'transaction': trx.__dict__,
            'signatures': [sig]
    }
    
  • v1/chain/push_transaction广播的json数据

    {
    	"compression": "none",
    	"transaction": {
    		"expiration": "2019-09-20T03:56:47.381481+00:00",
    		"ref_block_num": 32953,
    		"ref_block_prefix": 202153414,
    		"net_usage_words": 0,
    		"max_cpu_usage_ms": 0,
    		"delay_sec": 0,
    		"context_free_actions": [],
    		"actions": [{
    			"account": "eosio.token",
    			"name": "transfer",
    			"authorization": [{
    				"actor": "yangqingqin1",
    				"permission": "active"
    			}],
    			"data": "10a6b36c3acba6f1c0a6b36c3acba6f1d20400000000000004454f53000000000f454f5320746f20746865206d6f6f6e"
    		}],
    		"transaction_extensions": []
    	},
    	"signatures": ["SIG_K1_KduPr37mvwWdciVHhUU4g7LCzNDYvVZ4ytxBa8ZhERpycQtn5DrknjcYcedrxbTxuszDHMeCtRbr4aMsLMqFYdTC1c3iqM"]
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值