|
- script_to_hash() (in module pybtc)
+
+ - serialize() (pybtc.Transaction method)
- sign_message() (in module pybtc)
diff --git a/docs/build/html/index.html b/docs/build/html/index.html
index d1ff280..8570cf8 100644
--- a/docs/build/html/index.html
+++ b/docs/build/html/index.html
@@ -59,7 +59,7 @@
import pybtc
a = pybtc.Address()
print(a.address)
-print(a.private_key.wif())
+print(a.private_key.wif)
diff --git a/docs/build/html/objects.inv b/docs/build/html/objects.inv
index 7ecf781..b86be7d 100644
Binary files a/docs/build/html/objects.inv and b/docs/build/html/objects.inv differ
diff --git a/docs/build/html/searchindex.js b/docs/build/html/searchindex.js
index dc383c8..c10b989 100644
--- a/docs/build/html/searchindex.js
+++ b/docs/build/html/searchindex.js
@@ -1 +1 @@
-Search.setIndex({docnames:["address","block","classes","contributing","examples","functional","index","installation","transaction"],envversion:53,filenames:["address.rst","block.rst","classes.rst","contributing.rst","examples.rst","functional.rst","index.rst","installation.rst","transaction.rst"],objects:{"pybtc.Address":{address:[0,1,1,""],hash:[0,1,1,""],hash_hex:[0,1,1,""],private_key:[0,1,1,""],public_key:[0,1,1,""],redeem_script:[0,1,1,""],redeem_script_hex:[0,1,1,""],script_hash:[0,1,1,""],testnet:[0,1,1,""],type:[0,1,1,""],witness_version:[0,1,1,""]},"pybtc.PrivateKey":{compressed:[0,1,1,""],hex:[0,1,1,""],key:[0,1,1,""],testnet:[0,1,1,""],wif:[0,1,1,""]},"pybtc.PublicKey":{compressed:[0,1,1,""],hex:[0,1,1,""],key:[0,1,1,""],testnet:[0,1,1,""]},pybtc:{Address:[0,0,1,""],Block:[1,0,1,""],PrivateKey:[0,0,1,""],PublicKey:[0,0,1,""],Transaction:[8,0,1,""],address_to_hash:[5,2,1,""],address_to_script:[5,2,1,""],address_type:[5,2,1,""],bits_to_difficulty:[5,2,1,""],bits_to_target:[5,2,1,""],bytes_needed:[5,2,1,""],bytes_to_int:[5,2,1,""],c_int_len:[5,2,1,""],c_int_to_int:[5,2,1,""],create_private_key:[5,2,1,""],decode_script:[5,2,1,""],delete_from_script:[5,2,1,""],difficulty_to_target:[5,2,1,""],get_var_int_len:[5,2,1,""],hash_to_address:[5,2,1,""],int_to_bytes:[5,2,1,""],int_to_c_int:[5,2,1,""],int_to_var_int:[5,2,1,""],is_address_valid:[5,2,1,""],is_public_key_valid:[5,2,1,""],is_valid_signature_encoding:[5,2,1,""],is_wif_valid:[5,2,1,""],merkle_branches:[5,2,1,""],merkle_root:[5,2,1,""],merkleroot_from_branches:[5,2,1,""],parse_script:[5,2,1,""],private_key_to_wif:[5,2,1,""],private_to_public_key:[5,2,1,""],public_key_to_address:[5,2,1,""],read_var_int:[5,2,1,""],read_var_list:[5,2,1,""],reverse_hash:[5,2,1,""],rh2s:[5,2,1,""],s2rh:[5,2,1,""],script_to_hash:[5,2,1,""],sign_message:[5,2,1,""],target_to_difficulty:[5,2,1,""],var_int_len:[5,2,1,""],var_int_to_int:[5,2,1,""],verify_signature:[5,2,1,""],wif_to_private_key:[5,2,1,""]}},objnames:{"0":["py","class","Python class"],"1":["py","attribute","Python attribute"],"2":["py","function","Python function"]},objtypes:{"0":"py:class","1":"py:attribute","2":"py:function"},terms:{"03b8b44876e1f45be7e42953ea47026c39cc45341344d3ab32701b93de696107af":4,"0479f17a94410afd4f27588a192bacada53add0741765092dc0f8e2a29ea1bcd276dbc1ef74c3e0172d9db8047f2a0a5dc2e8e51a13f7f0cc072de906b765e0f7f":4,"17mxwxxzrmj1njjzdszzbw9ursaradeuat":4,"1chpkurzfhdculkanhcc3ra9kfxm2lrguw":4,"3bqeq3xql6azmk3bxnyr8vxgxutog63j4t":4,"5jw8dy1ubrd35xup6ed6klefa4ajfbx381":4,"5jw8dy1ubrd35xup6ed6klefa4ajfbx381hwuhvpgirjto9ztnr":4,"boolean":[0,5],"byte":[0,5],"case":[0,4,5],"class":[0,1,4,6,8],"default":[0,4,5],"float":5,"function":6,"import":[4,6],"new":[0,5],"public":[0,4,6,7],"return":5,"true":[0,4,5],"try":3,For:0,NOT:5,The:[0,1,4,6,7,8],Use:5,Using:5,abil:4,accord:[0,5],acord:6,add:3,address:[2,6],address_hash:5,address_net_typ:4,address_to_hash:5,address_to_script:5,address_typ:[0,4,5],addresshash:5,adopt:4,against:3,aiohttp:3,aleksei:6,all:3,allow:0,alreadi:[0,4],ani:7,asm:5,avail:6,avoid:5,backward:5,base58:[0,5],base:[0,5],base_byt:5,basic:6,bc1q6cxx5t8xkruz3s5khx7923xvsx5ry4c6p74m5:4,bech32:[0,5],big:5,bip141:6,bip32:6,bip39:6,bip44:6,bit:5,bitap:7,bitcoin:[0,6],bits_to_difficulti:5,bits_to_target:5,block:[2,4,6],branch:[3,5],bug:6,bui:4,button:3,byteord:5,bytes_need:5,bytes_to_int:5,bytesio:5,c_int_len:5,c_int_to_int:5,calcul:5,can:[4,7],capac:4,chang:[3,5],check:5,choic:7,clone:[3,7],coinbas:5,coinbase_hash:5,collect:0,com:7,command:7,commit:3,compat:5,compatibilitylegaci:5,compres:5,compress:[0,4,5],comress:0,constructor:6,contain:0,continu:6,contribut:6,contributor:6,control:4,convert:5,copi:7,correspond:[0,5],cost:4,count:5,cover:7,coverag:6,creat:[0,1,5,6,8],create_private_kei:[4,5],current:6,data:5,data_typ:5,decod:[5,8],decode_script:5,delete_from_script:5,depend:5,deprec:5,der:5,deseri:6,determin:0,determinist:6,develop:6,dictionari:5,difficulti:6,difficulty_to_target:5,document:7,don:[3,7],easili:7,ecdsa:5,emb:7,encod:[0,6],exampl:6,except:4,expand:4,fals:[0,4,5,8],fee:4,feel:6,file:[3,6],first:7,flag:[0,5],folder:3,fork:3,format:[0,4,5],found:6,free:6,freeli:6,from:[0,3,5,6],fromat:5,fund:5,futur:5,gener:6,get:5,get_var_int_len:5,git:7,github:[3,6,7],given:5,good:3,gpl:6,guid:7,hard:3,has:0,hash160:5,hash:[0,6],hash_hex:0,hash_str:5,hash_to_address:5,have:[0,3,4,6,7],hex:[0,4,5],hierarch:6,host:6,human:5,ignor:0,implement:[0,4,5,6],imporv:6,improv:6,initi:[0,6],insid:5,instanc:0,instans:0,instruct:6,int_to_byt:5,int_to_c_int:5,int_to_var_int:5,integ:5,intp:3,is_address_valid:[4,5],is_public_key_valid:5,is_valid_signature_encod:5,is_wif_valid:[4,5],issu:6,jto9ztnr:4,karpov:6,karybkin:6,kei:0,kyvzyvdzwd4jspft4wxwjg53as227zt2qiwbmticzeusjiwvbeqi:4,l5xkga2xehcinwepmyiabs1bqqux8av5dgvqcprtvjc3zcr5sxu:4,legaci:[4,5],len:5,length:[0,5],librari:7,link:3,list:5,littl:5,locat:3,locktim:8,log:3,loss:5,mainnet:4,make:3,master:3,mean:0,menu:3,merkl:6,merkle_branch:5,merkle_root:5,merkleroot_from_branch:5,messag:5,mine:6,miner:4,mnemon:6,mpr4hdfu269yxgztpvysd21gtpvdxptmh6:4,msg:5,must:5,nativ:4,necessari:5,need:[4,5],network:[0,5],non:4,none:[0,4,5,8],ntype:5,num:5,numer:5,object:[0,4],onc:7,one:0,onli:[0,5],opcod:5,open:3,option:[0,5],order:[3,5,6],own:[3,7],p2pkh:[0,4,5,6],p2sh:[0,5,6],p2sh_p2wpkh:[0,4,5],p2wpkh:[0,4,5,6],p2wsh:[0,5,6],packag:6,page:3,paradigm:5,paramet:[0,5],pars:5,parse_script:5,part:7,pass:3,perfom:6,pip:6,pleas:[3,6],pool:6,potenti:5,press:3,pretti:3,primit:[5,6],print:6,privat:[0,4,6],private_kei:[0,4,5,6],private_key_to_wif:5,private_to_public_kei:[4,5],privatekei:0,process:7,program:[0,5],project:6,properli:7,provid:0,pub:4,pub_kei:5,pubkei:[0,4,5,6],public_kei:[0,4],public_key_to_address:[4,5],publickei:[0,4],pull:[3,6],pure:6,pwpkh:[0,6],pybtc:[0,1,4,5,7,8],python3:7,python:[6,7],random:0,raw:5,raw_hash:5,raw_tx:8,read:5,read_var_int:5,read_var_list:5,readabl:5,recent:6,recogn:5,recommend:[0,4],record:3,redeeem:0,redeem_script:0,redeem_script_hex:0,reduc:4,refer:6,remov:5,repo:3,repositori:7,represent:5,reqsig:5,request:[3,6],requir:5,result:5,revers:5,reverse_hash:5,rh2:5,right:3,ripemd160:5,root:6,run:7,s2rh:5,script:[0,6],script_hash:[0,5],script_to_hash:5,secp256k1:[5,6],segreg:6,segwit:[0,4,5],send:6,set:[0,4,5],setup:7,sha256:5,sig:5,sign:5,sign_messag:5,signatur:6,simpl:7,simpli:7,site:7,softwar:7,some:6,sourc:[0,1,5,8],specifi:[0,5],sript:5,start:5,step:7,straightforward:3,stream:5,string:[0,5],sub_script:5,subscript:5,suggest:6,support:[0,4,6],sure:3,target:5,target_to_difficulti:5,termin:7,test:6,testnet:[0,4,5,8],thi:[0,4,5,6,7],through:7,tool:[4,6],tracker:6,traget:5,transact:[1,2,5,6],transacton:5,tx_format:8,tx_hash_list:5,type:[0,4,5,6],uncompress:[4,5],upper:3,usag:[4,6],use:[0,4,5],user:5,using:[5,7],valid:5,valu:5,var_int_len:5,var_int_to_int:5,variabl:5,verifi:5,verify_signatur:5,version:[0,5,6,8],vriabl:5,wallet:6,web:3,which:[4,5],wif:[0,4,5,6],wif_to_private_kei:5,wit:[0,4,5,6],witness_vers:[0,4,5],work:[0,4],workflow:3,wors:3,written:6,you:[0,4,6,7],your:[3,4,7]},titles:["Addresses","Blocks","Reference","Contributing","Examples","Pure functions reference","Welcome to PYBTC","Installation","Transactions"],titleterms:{"function":[4,5],"new":6,"public":5,address:[0,4,5],author:6,block:1,code:[6,7],content:6,contribut:3,contributor:3,coverag:3,creat:4,depend:6,difficulti:5,encod:5,exampl:4,featur:6,from:[4,7],get:[4,6,7],hash:5,instal:[6,7],instruct:3,kei:[4,5,6],librari:6,licens:6,merkl:5,packag:7,pip:7,privat:5,pure:[4,5],pybtc:6,quick:6,refer:[2,5],root:5,script:[4,5],signatur:5,sourc:[6,7],start:6,tabl:6,test:3,tool:5,transact:8,welcom:6,what:6}})
\ No newline at end of file
+Search.setIndex({docnames:["address","block","classes","contributing","examples","functional","index","installation","transaction"],envversion:53,filenames:["address.rst","block.rst","classes.rst","contributing.rst","examples.rst","functional.rst","index.rst","installation.rst","transaction.rst"],objects:{"pybtc.Address":{address:[0,1,1,""],hash:[0,1,1,""],hash_hex:[0,1,1,""],private_key:[0,1,1,""],public_key:[0,1,1,""],redeem_script:[0,1,1,""],redeem_script_hex:[0,1,1,""],script_hash:[0,1,1,""],testnet:[0,1,1,""],type:[0,1,1,""],witness_version:[0,1,1,""]},"pybtc.PrivateKey":{compressed:[0,1,1,""],hex:[0,1,1,""],key:[0,1,1,""],testnet:[0,1,1,""],wif:[0,1,1,""]},"pybtc.PublicKey":{compressed:[0,1,1,""],hex:[0,1,1,""],key:[0,1,1,""],testnet:[0,1,1,""]},"pybtc.Transaction":{decode:[8,2,1,""],encode:[8,2,1,""],json:[8,2,1,""],serialize:[8,2,1,""]},pybtc:{Address:[0,0,1,""],Block:[1,0,1,""],PrivateKey:[0,0,1,""],PublicKey:[0,0,1,""],Transaction:[8,0,1,""],address_to_hash:[5,3,1,""],address_to_script:[5,3,1,""],address_type:[5,3,1,""],bits_to_difficulty:[5,3,1,""],bits_to_target:[5,3,1,""],bytes_needed:[5,3,1,""],bytes_to_int:[5,3,1,""],c_int_len:[5,3,1,""],c_int_to_int:[5,3,1,""],create_private_key:[5,3,1,""],decode_script:[5,3,1,""],delete_from_script:[5,3,1,""],difficulty_to_target:[5,3,1,""],get_var_int_len:[5,3,1,""],hash_to_address:[5,3,1,""],int_to_bytes:[5,3,1,""],int_to_c_int:[5,3,1,""],int_to_var_int:[5,3,1,""],is_address_valid:[5,3,1,""],is_public_key_valid:[5,3,1,""],is_valid_signature_encoding:[5,3,1,""],is_wif_valid:[5,3,1,""],merkle_branches:[5,3,1,""],merkle_root:[5,3,1,""],merkleroot_from_branches:[5,3,1,""],parse_script:[5,3,1,""],private_key_to_wif:[5,3,1,""],private_to_public_key:[5,3,1,""],public_key_to_address:[5,3,1,""],read_var_int:[5,3,1,""],read_var_list:[5,3,1,""],reverse_hash:[5,3,1,""],rh2s:[5,3,1,""],s2rh:[5,3,1,""],script_to_hash:[5,3,1,""],sign_message:[5,3,1,""],target_to_difficulty:[5,3,1,""],var_int_len:[5,3,1,""],var_int_to_int:[5,3,1,""],verify_signature:[5,3,1,""],wif_to_private_key:[5,3,1,""]}},objnames:{"0":["py","class","Python class"],"1":["py","attribute","Python attribute"],"2":["py","method","Python method"],"3":["py","function","Python function"]},objtypes:{"0":"py:class","1":"py:attribute","2":"py:method","3":"py:function"},terms:{"03b8b44876e1f45be7e42953ea47026c39cc45341344d3ab32701b93de696107af":4,"0479f17a94410afd4f27588a192bacada53add0741765092dc0f8e2a29ea1bcd276dbc1ef74c3e0172d9db8047f2a0a5dc2e8e51a13f7f0cc072de906b765e0f7f":4,"17mxwxxzrmj1njjzdszzbw9ursaradeuat":4,"1chpkurzfhdculkanhcc3ra9kfxm2lrguw":4,"3bqeq3xql6azmk3bxnyr8vxgxutog63j4t":4,"5jw8dy1ubrd35xup6ed6klefa4ajfbx381":4,"5jw8dy1ubrd35xup6ed6klefa4ajfbx381hwuhvpgirjto9ztnr":4,"boolean":[0,5,8],"byte":[0,5,8],"case":[0,4,5],"class":[0,1,4,6,8],"default":[0,4,5,8],"float":5,"function":6,"import":[4,6],"int":8,"new":[0,5,8],"public":[0,4,6,7],"return":[5,8],"true":[0,4,5,8],"try":3,For:0,NOT:5,The:[0,1,4,6,7,8],Use:5,Using:5,abil:4,accord:[0,5],acord:6,add:3,address:[2,6,8],address_hash:5,address_net_typ:4,address_to_hash:5,address_to_script:5,address_typ:[0,4,5],addresshash:5,adopt:4,against:3,aiohttp:3,aleksei:6,all:[3,8],allow:0,alreadi:[0,4],also:[],ani:7,asm:[5,8],avail:6,avoid:5,backward:5,base58:[0,5],base68:8,base:[0,5],base_byt:5,basic:6,bc1q6cxx5t8xkruz3s5khx7923xvsx5ry4c6p74m5:4,bech32:[0,5,8],best:8,big:5,bip141:6,bip32:6,bip39:6,bip44:6,bit:5,bitap:7,bitcoin:[0,6],bits_to_difficulti:5,bits_to_target:5,block:[2,4,6],bool:8,branch:[3,5],bug:6,bui:4,button:3,byteord:5,bytes_need:5,bytes_to_int:5,bytesio:5,c_int_len:5,c_int_to_int:5,calcul:5,can:[4,7],capac:4,chang:[3,5,8],check:5,choic:7,clear:[],clone:[3,7],coinbas:5,coinbase_hash:5,collect:0,com:7,command:7,commit:3,compat:5,compatibilitylegaci:5,compres:5,compress:[0,4,5],comress:0,constructor:6,contain:0,continu:6,contribut:6,contributor:6,control:4,convert:5,copi:7,correspond:[0,5],cost:4,count:5,cover:7,coverag:6,creat:[0,1,5,6,8],create_private_kei:[4,5],current:6,data:5,data_typ:5,decod:[5,8],decode_script:5,delete_from_script:5,depend:5,deprec:5,der:5,deseri:6,determin:0,determinist:6,develop:6,dict:[],dictionari:5,difficulti:6,difficulty_to_target:5,document:7,doe:[],don:[3,7],easili:7,ecdsa:5,either:[],els:[],emb:7,empti:8,encod:[0,6,8],equal:[],exampl:6,except:4,expand:4,fals:[0,4,5,8],fee:4,feel:6,file:[3,6],first:7,flag:[0,5,8],folder:3,follow:[],fork:3,format:[0,4,5,8],found:6,free:6,freeli:6,from:[0,3,5,6,8],fromat:5,fromkei:[],fund:5,futur:5,gener:6,get:[5,8],get_var_int_len:5,git:7,github:[3,6,7],given:5,good:3,gpl:6,guid:7,hard:3,has:0,hash160:5,hash:[0,6],hash_hex:0,hash_str:5,hash_to_address:5,have:[0,3,4,6,7],hex:[0,4,5,8],hierarch:6,host:6,human:[5,8],ignor:0,implement:[0,4,5,6],imporv:6,improv:6,initi:[0,6],insid:5,instanc:0,instans:0,instruct:6,int_to_byt:5,int_to_c_int:5,int_to_var_int:5,integ:5,intp:3,is_address_valid:[4,5],is_public_key_valid:5,is_valid_signature_encod:5,is_wif_valid:[4,5],issu:6,item:[],iter:[],json:8,jto9ztnr:4,karpov:6,karybkin:6,kei:0,keyerror:[],kyvzyvdzwd4jspft4wxwjg53as227zt2qiwbmticzeusjiwvbeqi:4,l5xkga2xehcinwepmyiabs1bqqux8av5dgvqcprtvjc3zcr5sxu:4,lack:[],legaci:[4,5],len:5,length:[0,5],librari:7,like:[],link:3,list:5,littl:5,locat:3,lock:8,lock_tim:8,locktim:[],log:3,loss:5,mainnet:4,make:3,master:3,mean:[0,8],menu:3,merkl:6,merkle_branch:5,merkle_root:5,merkleroot_from_branch:5,messag:5,method:[],mine:6,miner:4,mnemon:6,mpr4hdfu269yxgztpvysd21gtpvdxptmh6:4,msg:5,must:5,nativ:4,necessari:5,need:[4,5],network:[0,5],non:4,none:[0,4,5,8],ntype:5,num:5,numer:5,object:[0,4,8],onc:7,one:0,onli:[0,5],opcod:[5,8],open:3,option:[0,5,8],order:[3,5,6],otherwis:[],own:[3,7],p2pkh:[0,4,5,6],p2sh:[0,5,6],p2sh_p2wpkh:[0,4,5],p2wpkh:[0,4,5,6],p2wsh:[0,5,6],packag:6,page:3,pair:[],paradigm:5,paramet:[0,5,8],pars:5,parse_script:5,part:[7,8],pass:3,perfom:6,perform:8,pip:6,pleas:[3,6],pool:6,pop:[],popitem:[],potenti:5,present:[],press:3,pretti:3,primit:[5,6],print:6,privat:[0,4,6],private_kei:[0,4,5,6],private_key_to_wif:5,private_to_public_kei:[4,5],privatekei:0,process:7,program:[0,5],project:6,properli:7,properti:8,provid:[0,8],pub:4,pub_kei:5,pubkei:[0,4,5,6],public_kei:[0,4],public_key_to_address:[4,5],publickei:[0,4],pull:[3,6],pure:6,pwpkh:[0,6],pybtc:[0,1,4,5,7,8],python3:7,python:[6,7],rais:[],random:0,raw:[5,8],raw_hash:5,raw_tx:8,read:5,read_var_int:5,read_var_list:5,readabl:[5,8],recent:6,recogn:5,recommend:[0,4],record:3,redeeem:0,redeem_script:0,redeem_script_hex:0,reduc:4,refer:6,remov:5,repo:3,repositori:7,repres:8,represent:[5,8],reqsig:5,request:[3,6],requir:5,result:5,revers:5,reverse_hash:5,rh2:5,right:3,ripemd160:5,root:6,run:7,s2rh:5,script:[0,6],script_hash:[0,5],script_to_hash:5,secp256k1:[5,6],segreg:6,segwit:[0,4,5,8],send:6,serial:8,set:[0,4,5,8],setdefault:[],setup:7,sha256:5,shallow:[],sig:5,sign:5,sign_messag:5,signatur:6,simpl:7,simpli:7,site:7,softwar:7,some:6,sourc:[0,1,5,8],specifi:[0,5],sript:5,start:5,step:7,str:8,straightforward:3,stream:5,string:[0,5,8],strip:8,sub_script:5,subscript:5,suggest:6,support:[0,4,6],sure:3,target:5,target_to_difficulti:5,templat:8,termin:7,test:6,testnet:[0,4,5,8],thi:[0,4,5,6,7],through:7,time:8,tool:[4,6],tracker:6,traget:5,transacion:8,transact:[1,2,5,6],transacton:5,tupl:[],tx_format:8,tx_hash_list:5,type:[0,4,5,6,8],uncompress:[4,5],updat:[],upper:3,usag:[4,6],use:[0,4,5],used:8,user:5,using:[5,7,8],valid:5,valu:5,var_int_len:5,var_int_to_int:5,variabl:5,verifi:5,verify_signatur:5,version:[0,5,6,8],view:[],vriabl:5,wallet:6,web:3,well:8,which:[4,5],wif:[0,4,5,6],wif_to_private_kei:5,wit:[0,4,5,6],witness_vers:[0,4,5],work:[0,4],workflow:3,wors:3,written:6,you:[0,4,6,7],your:[3,4,7]},titles:["Addresses","Blocks","Reference","Contributing","Examples","Pure functions reference","Welcome to PYBTC","Installation","Transactions"],titleterms:{"function":[4,5],"new":6,"public":5,address:[0,4,5],author:6,block:1,code:[6,7],content:6,contribut:3,contributor:3,coverag:3,creat:4,depend:6,difficulti:5,encod:5,exampl:4,featur:6,from:[4,7],get:[4,6,7],hash:5,instal:[6,7],instruct:3,kei:[4,5,6],librari:6,licens:6,merkl:5,packag:7,pip:7,privat:5,pure:[4,5],pybtc:6,quick:6,refer:[2,5],root:5,script:[4,5],signatur:5,sourc:[6,7],start:6,tabl:6,test:3,tool:5,transact:8,welcom:6,what:6}})
\ No newline at end of file
diff --git a/docs/build/html/transaction.html b/docs/build/html/transaction.html
index e450a7b..e7f3102 100644
--- a/docs/build/html/transaction.html
+++ b/docs/build/html/transaction.html
@@ -39,8 +39,79 @@
The class for creating transaction.
-
-class
pybtc.Transaction(raw_tx=None, tx_format='decoded', version=1, lockTime=0, testnet=False)[source]
-
+class pybtc.Transaction(raw_tx=None, tx_format='decoded', version=1, lock_time=0, testnet=False)[source]
+The class for Transaction object
+
+
+
+
+| Parameters: |
+- raw_tx – (optional) raw transaction in bytes or HEX encoded string, if no raw transaction provided
+well be created new empty transaction template.
+- tx_format – “raw” or “decoded” format. Raw format is mean that all transaction represented in bytes
+for best performance.
+Decoded transaction is represented in human readable format using base68, hex, bech32,
+asm and opcodes. By default “decoded” format using.
+- version (int) – transaction version for new template, by default 1.
+- lock_time (int) – transaction lock time for new template, by default 0.
+- testnet (boolean) – address type for “decoded” transaction representation.
+
+ |
+
+
+
+
+-
+
decode(testnet=None)[source]
+change Transacion object representation to “decoded” human readable format
+
+
+
+
+| Parameters: | testnet (bool) – (optional) address type for “decoded” transaction representation, by default None.
+if None used type from transaction property “format”. |
+
+
+
+
+
+
+-
+
encode()[source]
+change Transaction object representation to “raw” bytes format,
+all human readable part will be stripped.
+
+
+
+-
+
json()[source]
+Get json transaction representation
+
+
+
+-
+
serialize(segwit=True, hex=True)[source]
+Get serialized transaction
+
+
+
+
+| Parameters: |
+- segwit (bool) – (optional) flag for segwit representation of serialized transaction, by
+default True.
+- hex (bool) – (optional) if set to True return HEX encoded string, by default True.
+
+ |
+
+| Return str,bytes: |
+| | serialized transaction in HEX or bytes.
+ |
+
+
+
+
+
+
diff --git a/docs/source/transaction.rst b/docs/source/transaction.rst
index ac2360c..1da13cb 100644
--- a/docs/source/transaction.rst
+++ b/docs/source/transaction.rst
@@ -6,6 +6,7 @@ The class for creating transaction.
.. autoclass:: pybtc.Transaction
- :members:
+ :members:
+
diff --git a/pybtc/address.py b/pybtc/address.py
index b67b2c1..2eccdfe 100644
--- a/pybtc/address.py
+++ b/pybtc/address.py
@@ -43,10 +43,11 @@ class PrivateKey():
self.hex = hexlify(self.key).decode()
self.wif = private_key_to_wif(self.key, compressed, testnet)
return
- assert isinstance(key, str)
+ if not isinstance(key, str) or not is_wif_valid(key):
+ raise TypeError("private key invalid")
+
self.key = wif_to_private_key(key, hex=False)
self.hex = hexlify(self.key).decode()
- self.wif = private_key_to_wif(self.key, compressed, testnet)
if key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX,
TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX):
self.compressed = False
@@ -57,6 +58,7 @@ class PrivateKey():
self.testnet = True
else:
self.testnet = False
+ self.wif = private_key_to_wif(self.key, self.compressed, self.testnet)
def __str__(self):
return self.wif
@@ -201,15 +203,18 @@ class Address():
class ScriptAddress():
- def __init__(self, script, address_type="P2SH",
- testnet=False, witness_version=None):
+ def __init__(self, script,
+ testnet=False, witness_version=0):
self.witness_version = witness_version
self.testnet = testnet
if isinstance(script, str):
script = unhexlify(script)
self.script = script
self.script_hex = hexlify(self.script).decode()
- self.hash = hash160(self.script)
+ if witness_version is None:
+ self.hash = hash160(self.script)
+ else:
+ self.hash = sha256(self.script)
self.script_opcodes = decode_script(self.script)
self.script_opcodes_asm = decode_script(self.script, 1)
self.address = hash_to_address(self.hash,
@@ -217,3 +222,49 @@ class ScriptAddress():
witness_version=self.witness_version,
testnet=self.testnet)
+ @classmethod
+ def multisig(cls, n, m, public_key_list,
+ testnet=False, witness_version=0):
+ """
+ The class method for creating a multisig address.
+
+ :param n: count of required signatures (max 15).
+ :param m: count of total addresses of participants (max 15).
+ :param list address_list: addresses list, allowed types:
+
+ - bytes or HEX encoded private key
+ - private key in WIF format
+ - PrivateKey instance,
+ - bytes or HEX encoded public key
+ - PublicKey instance
+
+
+ """
+ if n > 15 or m > 15 or n > m or n < 1 or m < 1:
+ raise TypeError("invalid n of m maximum 15 of 15 multisig allowed")
+ if len(public_key_list) != m:
+ raise TypeError("invalid address list count")
+ script = bytes([0x50 + n])
+ for a in list(public_key_list):
+ if isinstance(a, str):
+ try:
+ a = unhexlify(a)
+ except:
+ if is_wif_valid(a):
+ a = private_to_public_key(a, hex=False)
+ pass
+ if isinstance(a, Address):
+ a = a.public_key.key
+ elif isinstance(a, PublicKey):
+ a = a.key
+ elif isinstance(a, PrivateKey):
+ a = private_to_public_key(a.key)
+ if not isinstance(a, bytes):
+ raise TypeError("invalid public key list element")
+ if len(a) == 32:
+ a = private_to_public_key(a)
+ if len(a) != 33:
+ raise TypeError("invalid public key list element size")
+ script += int_to_var_int(len(a)) + a
+ script += bytes([0x50 + m]) + OP_CHECKMULTISIG
+ return cls(script, testnet=testnet, witness_version=witness_version)
diff --git a/pybtc/opcodes.py b/pybtc/opcodes.py
index 93d9162..b10c147 100644
--- a/pybtc/opcodes.py
+++ b/pybtc/opcodes.py
@@ -11,8 +11,8 @@ OPCODE["OP_PUSHDATA2"] = 0x4d
OPCODE["OP_PUSHDATA4"] = 0x4e
OPCODE["OP_1NEGATE"] = 0x4f
OPCODE["OP_RESERVED"] = 0x50
-OPCODE["OP_1"] = 0x51
OPCODE["OP_TRUE"] = 0x51
+OPCODE["OP_1"] = 0x51
OPCODE["OP_2"] = 0x52
OPCODE["OP_3"] = 0x53
OPCODE["OP_4"] = 0x54
diff --git a/pybtc/tools.py b/pybtc/tools.py
index a3808a3..907cfb3 100644
--- a/pybtc/tools.py
+++ b/pybtc/tools.py
@@ -438,6 +438,13 @@ def get_witness_version(address):
# Script
+def public_key_to_pubkey_script(key, hex=True):
+ if isinstance(key, str):
+ key = unhexlify(key)
+ s = bytes([len(key)]) + key + OP_CHECKSIG
+ return hexlify(s).decode() if hex else s
+
+
def parse_script(script, segwit=True):
"""
Parse script and return script type, script address and required signatures count.
@@ -552,9 +559,10 @@ def decode_script(script, asm=False):
Decode script to ASM format or to human readable OPCODES string.
:param script: script in bytes string or HEX encoded string format.
- :param asm: (optional) If set to True decode to ASM fromat, by default set to False.
+ :param asm: (optional) If set to True decode to ASM format, by default set to False.
:return: script in ASM format string or OPCODES string.
"""
+
if isinstance(script, str):
try:
script = unhexlify(script)
@@ -573,14 +581,35 @@ def decode_script(script, asm=False):
result.append('[%s]' % script[s])
s += script[s] + 1
continue
- elif script[s] == OPCODE["OP_PUSHDATA1"]:
- s += 1 + script[s + 1]
+
+ if script[s] == OPCODE["OP_PUSHDATA1"]:
+ ld = script[s + 1]
+ if asm:
+ result.append(hexlify(script[s + 1:s + 1 + ld]).decode())
+ else:
+ result.append(RAW_OPCODE[script[s]])
+ result.append('[%s]' % ld)
+ s += 1 + script[s + 1] + 1
elif script[s] == OPCODE["OP_PUSHDATA2"]:
- s += 2 + struct.unpack('= 0):
+ raise TypeError("v_out invalid")
+ if not isinstance(sequence, int) or not (sequence <= 0xffffffff and sequence >= 0):
+ raise TypeError("sequence invalid")
+
+ if private_key:
+ if not isinstance(private_key, PrivateKey):
+ private_key = PrivateKey(private_key)
+ if amount:
+ if not isinstance(amount, int) or not amount >= 0 and amount <= MAX_AMOUNT:
+ raise TypeError("amount invalid")
+
+ if tx_in_witness:
+ if not isinstance(tx_in_witness, list):
+ raise TypeError("tx_in_witness invalid")
+ l = 0
+ witness = []
+ for w in tx_in_witness:
+ if isinstance(w, str):
+ witness.append(unhexlify(w) if self["format"] == "raw" else w)
else:
- self["coinbase"] = False
- if sw:
- self["segwit"] = True
- self["hash"] = double_sha256(b)
- self["txId"] = double_sha256(b[:4] + b[6:sw] + b[-4:])
+ witness.append(w if self["format"] == "raw" else unhexlify(w))
+ l += 1 + len(w)
+ if len(w) >= 0x4c:
+ l += 1
+ if len(w) > 0xff:
+ l += 1
+ # witness script limit
+ if not l <= 10000:
+ raise TypeError("tx_in_witness invalid")
+
+ if tx_id == b"\x00" * 32:
+ if not (v_out == 0xffffffff and sequence == 0xffffffff and len(script_sig) <= 100):
+ raise TypeError("coinbase tx invalid")
+ self["coinbase"] = True
+
+ # script_pub_key
+ if script_pub_key:
+ if isinstance(script_pub_key, str):
+ script_pub_key = unhexlify(script_pub_key)
+ if not isinstance(script_pub_key, bytes):
+ raise TypeError("script_pub_key tx invalid")
+
+ if redeem_script:
+ if isinstance(redeem_script, str):
+ redeem_script = unhexlify(redeem_script)
+ if not isinstance(redeem_script, bytes):
+ raise TypeError("redeem_script tx invalid")
+
+ if address is not None:
+ if isinstance(address, str):
+ net = True if address_net_type(address) == 'mainnet' else False
+ if not net != self["testnet"]:
+ raise TypeError("address invalid")
+ script = address_to_script(address)
+ elif type(address) in (Address, ScriptAddress):
+ script = address_to_script(address.address)
+ if script_pub_key:
+ assert script_pub_key == script
+ else:
+ script_pub_key = script
+
+ k = len(self["vIn"])
+ self["vIn"][k] = dict()
+ self["vIn"][k]["vOut"] = v_out
+ self["vIn"][k]["sequence"] = sequence
+ if self["format"] == "raw":
+ self["vIn"][k]["txId"] = tx_id
+ self["vIn"][k]["scriptSig"] = script_sig
+ if script_pub_key:
+ self["vIn"][k]["scriptPubKey"] = script_pub_key
+ if redeem_script:
+ self["vIn"][k]["redeemScript"] = redeem_script
+ else:
+ self["vIn"][k]["txId"] = rh2s(tx_id)
+ self["vIn"][k]["scriptSig"] = hexlify(script_sig).decode()
+ self["vIn"][k]["scriptSigOpcodes"] = decode_script(script_sig)
+ self["vIn"][k]["scriptSigAsm"] = decode_script(script_sig, 1)
+ if script_pub_key:
+ self["vIn"][k]["scriptPubKey"] = hexlify(script_pub_key).decode()
+ self["vIn"][k]["scriptPubKeyOpcodes"] = decode_script(script_pub_key)
+ self["vIn"][k]["scriptPubKeyAsm"] = decode_script(script_pub_key, 1)
+ if redeem_script:
+ self["vIn"][k]["redeemScript"] = hexlify(redeem_script).decode()
+ self["vIn"][k]["redeemScriptOpcodes"] = decode_script(redeem_script)
+ self["vIn"][k]["redeemScriptAsm"] = decode_script(script_pub_key, 1)
+ if tx_in_witness:
+ self["segwit"] = True
+ self["vIn"][k]["txInWitness"] = witness
+ if amount:
+ self["vIn"][k]["value"] = amount
+ if private_key:
+ self["vIn"][k].private_key = private_key
+ self.__refresh__()
+ return self
+
+ def add_output(self, amount, address=None, script_pub_key=None):
+ assert address is not None or script_pub_key is not None
+ assert not (address is None and script_pub_key is None)
+ assert type(amount) == int
+ assert amount >= 0 and amount <= MAX_AMOUNT
+ if script_pub_key:
+ if type(script_pub_key) == str:
+ script_pub_key = unhexlify(script_pub_key)
+ assert type(script_pub_key) == bytes
+ else:
+ if type(address) == Address:
+ address = address.address
+ script_pub_key = address_to_script(address)
+
+ k = len(self["vOut"])
+ self["vOut"][k] = dict()
+ self["vOut"][k]["value"] = amount
+ segwit = True if "segwit" in self else False
+ s = parse_script(script_pub_key, segwit)
+ self["vOut"][k]["nType"] = s["nType"]
+ self["vOut"][k]["type"] = s["type"]
+
+ if self["format"] == "raw":
+ self["vOut"][k]["scriptPubKey"] = script_pub_key
+ if self["data"] is None:
+ if s["nType"] == 3:
+ self["data"] = s["data"]
+ if s["nType"] not in (3, 4, 7):
+ self["vOut"][k]["addressHash"] = s["addressHash"]
+ self["vOut"][k]["reqSigs"] = s["reqSigs"]
+ else:
+ self["vOut"][k]["scriptPubKey"] = hexlify(script_pub_key).decode()
+ if self["data"] is None:
+ if s["nType"] == 3:
+ self["data"] = hexlify(s["data"]).decode()
+ if s["nType"] not in (3, 4, 7):
+ self["vOut"][k]["addressHash"] = hexlify(s["addressHash"]).decode()
+ self["vOut"][k]["reqSigs"] = s["reqSigs"]
+ self["vOut"][k]["scriptPubKeyOpcodes"] = decode_script(script_pub_key)
+ self["vOut"][k]["scriptPubKeyAsm"] = decode_script(script_pub_key, 1)
+ sh = True if self["vOut"][k]["nType"] in (1, 5) else False
+ witness_version = None if self["vOut"][k]["nType"] < 5 else 0
+ if "addressHash" in self["vOut"][k]:
+ self["vOut"][k]["address"] = hash_to_address(self["vOut"][k]["addressHash"],
+ self["testnet"],
+ sh,
+ witness_version)
+ self.__refresh__()
+ return self
+
+ def del_output(self, n=None):
+ if not self["vOut"]:
+ return self
+ if n is None:
+ n = len(self["vOut"]) - 1
+ new_out = dict()
+ c = 0
+ for i in range(len(self["vOut"])):
+ if i != n:
+ new_out[c] = self["vOut"][i]
+ c += 1
+ self["vOut"] = new_out
+ self.__refresh__()
+ return self
+
+ def del_input(self, n):
+ if not self["vIn"]:
+ return self
+ if n is None:
+ n = len(self["vIn"]) - 1
+ new_in = dict()
+ c = 0
+ for i in range(len(self["vIn"])):
+ if i != n:
+ new_in[c] = self["vIn"][i]
+ c += 1
+ self["vIn"] = new_in
+ self.__refresh__()
+ return self
+
+ def sign_input(self, n, private_key=None, script_pub_key=None,
+ redeem_script=None,
+ sighash_type=SIGHASH_ALL,
+ address=None, amount=None, witness_version=0,
+ p2sh_p2wsh=False):
+ # private key
+ if not private_key:
+ try:
+ private_key = self["vIn"][n].private_key.key
+ except:
+ raise RuntimeError("no private key")
+ if isinstance(private_key, list):
+ public_key = [PublicKey(p).key for p in private_key]
+ private_key = [p.key if isinstance(p, PrivateKey) else PrivateKey(p).key for p in private_key]
+ else:
+ if not isinstance(private_key, PrivateKey):
+ private_key = PrivateKey(private_key)
+ public_key = [PublicKey(private_key).key]
+ private_key = [private_key.key]
+
+ if address is not None:
+ if isinstance(address, str):
+ net = True if address_net_type(address) == 'mainnet' else False
+ if not net != self["testnet"]:
+ raise TypeError("address invalid")
+ script_pub_key = address_to_script(address)
+ elif type(address) in (Address, ScriptAddress):
+ script_pub_key = address_to_script(address.address)
+ # script pub key
+ if script_pub_key is None:
+
+ if "scriptPubKey" in self["vIn"][n]:
+ script_pub_key = self["vIn"][n]["scriptPubKey"]
+ st = parse_script(script_pub_key)
+ elif redeem_script or "redeemScript" in self["vIn"][n]:
+ if witness_version is None or p2sh_p2wsh:
+ st = {"type": "P2SH"}
+ elif witness_version == 0:
+ st = {"type": "P2WSH"}
else:
- self["segwit"] = False
- self["txId"] = double_sha256(b)
- self["hash"] = self["txId"]
+ raise RuntimeError("not implemented")
+ else:
+ raise RuntimeError("no scriptPubKey key")
+ else:
+ st = parse_script(script_pub_key)
+ if isinstance(script_pub_key, str):
+ script_pub_key = unhexlify(script_pub_key)
- def decode(self, testnet=None):
- if self["format"] == "decoded":
- self.encode()
- self["format"] = "decoded"
- if testnet is not None:
- self["testnet"] = testnet
- if type(self["txId"]) == bytes:
- self["txId"] = rh2s(self["txId"])
- if "flag" in self:
- if type(self["flag"]) == bytes:
- self["flag"] = rh2s(self["flag"])
- if type(self["hash"]) == bytes:
- self["hash"] = rh2s(self["hash"])
- if type(self["rawTx"]) == bytes:
- self["rawTx"] = hexlify(self["rawTx"]).decode()
- for i in self["vIn"]:
- if type(self["vIn"][i]["txId"]) == bytes:
- self["vIn"][i]["txId"] = rh2s(self["vIn"][i]["txId"])
- if type(self["vIn"][i]["scriptSig"]) == bytes:
- self["vIn"][i]["scriptSig"] = hexlify(self["vIn"][i]["scriptSig"]).decode()
- try:
- t = list()
- for w in self["vIn"][i]["txInWitness"]:
- if type(w) == bytes:
- w = hexlify(w).decode()
- t.append(w)
- self["vIn"][i]["txInWitness"] = t
- self["vIn"][i]["txInWitnessAsm"] = [decode_script(ws, 1) for ws in
- self["vIn"][i]["txInWitness"]]
- self["vIn"][i]["txInWitnessOpcodes"] = [decode_script(ws) for ws in
- self["vIn"][i]["txInWitness"]]
- except:
- pass
- try:
- if type(self["vIn"][i]["addressHash"]) == bytes:
- self["vIn"][i]["addressHash"] = hexlify(self["vIn"][i]["addressHash"]).decode()
- sh = True if self["vIn"][i]["nType"] in (1, 5) else False
- witness_version = None if self["vIn"][i]["nType"] < 5 else 0
- self["vIn"][i]["address"] = hash_to_address(self["vIn"][i]["addressHash"],
- self["testnet"],
- sh,
- witness_version)
- except:
- pass
- if "scriptPubKey" in self["vIn"][i]:
- if type(self["vIn"][i]["scriptPubKey"]) == bytes:
- self["vIn"][i]["scriptPubKey"] = hexlify(self["vIn"][i]["scriptPubKey"]).decode()
- self["vIn"][i]["scriptPubKeyOpcodes"] = decode_script(self["vIn"][i]["scriptPubKey"])
- self["vIn"][i]["scriptPubKeyAsm"] = decode_script(self["vIn"][i]["scriptPubKey"], 1)
- if "redeemScript" in self["vIn"][i]:
- if type(self["vIn"][i]["redeemScript"]) == bytes:
- self["vIn"][i]["redeemScript"] = hexlify(self["vIn"][i]["redeemScript"]).decode()
- self["vIn"][i]["redeemScriptOpcodes"] = decode_script(self["vIn"][i]["redeemScript"])
- self["vIn"][i]["redeemScriptAsm"] = decode_script(self["vIn"][i]["redeemScript"], 1)
- if not self["coinbase"]:
- if type(self["vIn"][i]["scriptSig"]) == bytes:
- self["vIn"][i]["scriptSig"] = hexlify(self["vIn"][i]["scriptSig"]).decode()
- self["vIn"][i]["scriptSigOpcodes"] = decode_script(self["vIn"][i]["scriptSig"])
- self["vIn"][i]["scriptSigAsm"] = decode_script(self["vIn"][i]["scriptSig"], 1)
+ # sign input
+ if st["type"] == "PUBKEY":
+ script_sig = self.__sign_pubkey__(n, private_key, script_pub_key, sighash_type)
+ elif st["type"] == "P2PKH":
+ script_sig = self.__sign_p2pkh__(n, private_key, public_key, script_pub_key, sighash_type)
+ elif st["type"] == "P2SH":
+ script_sig = self.__sign_p2sh(n, private_key, public_key, redeem_script, sighash_type, amount, p2sh_p2wsh)
+ elif st["type"] == "P2WPKH":
+ script_sig = self.__sign_p2wpkh(n, private_key, public_key, script_pub_key, sighash_type, amount)
+ elif st["type"] == "P2WSH":
+ script_sig = self.__sign_p2wsh(n, private_key, public_key, script_pub_key,
+ redeem_script, sighash_type, amount)
+ else:
+ raise RuntimeError("not implemented")
- for i in self["vOut"]:
- if type(self["vOut"][i]["scriptPubKey"]) == bytes:
- self["vOut"][i]["scriptPubKey"] = hexlify(self["vOut"][i]["scriptPubKey"]).decode()
- try:
- if type(self["vOut"][i]["addressHash"]) == bytes:
- self["vOut"][i]["addressHash"] = hexlify(self["vOut"][i]["addressHash"]).decode()
- sh = True if self["vOut"][i]["nType"] in (1, 5) else False
- witness_version = None if self["vOut"][i]["nType"] < 5 else 0
- self["vOut"][i]["address"] = hash_to_address(self["vOut"][i]["addressHash"],
- self["testnet"],
- sh,
- witness_version)
- except:
- pass
- self["vOut"][i]["scriptPubKeyOpcodes"] = decode_script(self["vOut"][i]["scriptPubKey"])
- self["vOut"][i]["scriptPubKeyAsm"] = decode_script(self["vOut"][i]["scriptPubKey"], 1)
- if "data" in self:
- if type(self["data"]) == bytes:
- self["data"] = hexlify(self["data"]).decode()
- return self
+ if self["format"] == "raw":
+ self["vIn"][n]["scriptSig"] = script_sig
+ else:
+ self["vIn"][n]["scriptSig"] = hexlify(script_sig).decode()
+ self["vIn"][n]["scriptSigOpcodes"] = decode_script(script_sig)
+ self["vIn"][n]["scriptSigAsm"] = decode_script(script_sig, 1)
+ self.__refresh__()
+ return self
- def encode(self):
- if type(self["txId"]) == str:
- self["txId"] = s2rh(self["txId"])
- if "flag" in self:
- if type(self["flag"]) == str:
- self["flag"] = s2rh(self["flag"])
- if type(self["hash"]) == str:
- self["hash"] = s2rh(self["hash"])
- if type(self["rawTx"]) == str:
- self["rawTx"] = unhexlify(self["rawTx"])
+ def __sign_pubkey__(self, n, private_key, script_pub_key, sighash_type):
+ sighash = self.sig_hash(n, script_pub_key=script_pub_key, sighash_type=sighash_type)
+ sighash = s2rh(sighash) if isinstance(sighash, str) else sighash
+ signature = sign_message(sighash, private_key[0], 0) + bytes([sighash_type])
+ return b''.join([bytes([len(signature)]), signature])
- for i in self["vIn"]:
- if type(self["vIn"][i]["txId"]) == str:
- self["vIn"][i]["txId"] = s2rh(self["vIn"][i]["txId"])
- if type(self["vIn"][i]["scriptSig"]) == str:
- self["vIn"][i]["scriptSig"] = unhexlify(self["vIn"][i]["scriptSig"])
- try:
- t = list()
- for w in self["vIn"][i]["txInWitness"]:
- if type(w) == str:
- w = unhexlify(w)
- t.append(w)
- self["vIn"][i]["txInWitness"] = t
- if "txInWitnessAsm" in self["vIn"][i]:
- del self["vIn"][i]["txInWitnessAsm"]
- if "txInWitnessOpcodes" in self["vIn"][i]:
- del self["vIn"][i]["txInWitnessOpcodes"]
- except:
- pass
- try:
- if type(self["vIn"][i]["addressHash"]) == str:
- self["vIn"][i]["addressHash"] = unhexlify(self["vIn"][i]["addressHash"])
- if "address" in self["vIn"][i]:
- del self["vIn"][i]["address"]
- except:
- pass
- if "scriptSigAsm" in self["vIn"][i]:
- del self["vIn"][i]["scriptSigAsm"]
- if "scriptSigOpcodes" in self["vIn"][i]:
- del self["vIn"][i]["scriptSigOpcodes"]
+ def __sign_p2pkh__(self, n, private_key, public_key, script_pub_key, sighash_type):
+ sighash = self.sig_hash(n, script_pub_key=script_pub_key, sighash_type=sighash_type)
+ sighash = s2rh(sighash) if isinstance(sighash, str) else sighash
+ signature = sign_message(sighash, private_key[0], 0) + bytes([sighash_type])
+ script_sig = b''.join([bytes([len(signature)]),
+ signature,
+ bytes([len(public_key[0])]),
+ public_key[0]])
+ return script_sig
- for i in self["vOut"]:
- if type(self["vOut"][i]["scriptPubKey"]) == str:
- self["vOut"][i]["scriptPubKey"] = unhexlify(self["vOut"][i]["scriptPubKey"])
- try:
- if type(self["vOut"][i]["addressHash"]) == str:
- self["vOut"][i]["addressHash"] = unhexlify(self["vOut"][i]["addressHash"])
- if "address" in self["vOut"][i]:
- del self["vOut"][i]["address"]
- except:
- pass
- if "scriptPubKeyOpcodes" in self["vOut"][i]:
- del self["vOut"][i]["scriptPubKeyOpcodes"]
- if "scriptPubKeyAsm" in self["vOut"][i]:
- del self["vOut"][i]["scriptPubKeyAsm"]
+ def __sign_p2sh(self, n, private_key, public_key, redeem_script, sighash_type, amount, p2sh_p2wsh):
+ if not redeem_script:
+ if "redeemScript" in self["vIn"][n]:
+ redeem_script = self["vIn"][n]["redeemScript"]
+ else:
+ raise RuntimeError("no redeem script")
+ if isinstance(redeem_script, str):
+ redeem_script = unhexlify(redeem_script)
+ rst = parse_script(redeem_script)
- if "data" in self:
- if type(self["data"]) == str:
- self["data"] = unhexlify(self["data"])
- self["format"] = "raw"
- return self
+ if p2sh_p2wsh:
+ return self.__sign_p2sh_p2wsh(n, private_key,
+ public_key, redeem_script, sighash_type, amount)
+ if rst["type"] == "MULTISIG":
+ return self.__sign_p2sh_multisig(n, private_key, public_key, redeem_script, sighash_type)
+ elif rst["type"] == "P2WPKH":
+ return self.__sign_p2sh_p2wpkh(n, private_key, public_key, redeem_script, sighash_type, amount)
+ else:
+ return self.__sign_p2sh_custom(n, private_key, public_key, redeem_script, sighash_type, amount)
- def get_stream(self, stream):
- if type(stream) != io.BytesIO:
- if type(stream) == str:
- stream = unhexlify(stream)
- if type(stream) == bytes:
- stream = io.BytesIO(stream)
- else:
- raise TypeError
- return stream
+ def __sign_p2sh_multisig(self, n, private_key, public_key, redeem_script, sighash_type):
+ sighash = self.sig_hash(n, script_pub_key=redeem_script, sighash_type=sighash_type)
+ sighash = s2rh(sighash) if isinstance(sighash, str) else sighash
+ sig = [sign_message(sighash, p, 0) + bytes([sighash_type]) for p in private_key]
+ return b''.join(self.__get_multisig_script_sig__(self["vIn"][n]["scriptSig"],
+ public_key, sig,
+ redeem_script,
+ redeem_script,
+ n))
- def serialize(self, segwit=True, hex=True):
- chunks = []
- chunks.append(struct.pack('= 0
- assert type(sequence) == int
- assert sequence <= 0xffffffff and sequence >= 0
- assert type(script_sig) == bytes
- assert len(script_sig) <= 520
- if private_key:
- if type(private_key) != PrivateKey:
- private_key = PrivateKey(private_key)
- if amount:
- assert type(amount) == int
- assert amount >= 0 and amount <= MAX_AMOUNT
- if tx_in_witness:
- assert type(tx_in_witness) == list
- l = 0
- witness = []
- for w in tx_in_witness:
- if type(w) == str:
- witness.append(unhexlify(w) if self["format"] == "raw" else w)
- else:
- witness.append(w if self["format"] == "raw" else unhexlify(w))
- l += 1 + len(w)
- if len(w) >= 0x4c:
- l += 1
- if len(w) > 0xff:
- l += 1
- # witness script limit
- assert l <= 10000
- if tx_id == b"\x00" * 32:
- assert v_out == 0xffffffff and sequence == 0xffffffff and len(script_sig) <= 100
- self["coinbase"] = True
+ def sig_hash(self, n, script_pub_key=None, sighash_type=SIGHASH_ALL):
+ # check n
+ assert n >= 0
+ tx_in_count = len(self["vIn"])
- # script_pub_key
- if script_pub_key:
- if type(script_pub_key) == str:
- script_pub_key = unhexlify(script_pub_key)
- type(script_pub_key) == bytes
- if address is not None:
- if type(address) == str:
- net = True if address_net_type(address) == 'mainnet' else False
- assert not net == self["testnet"]
- script = address_to_script(address)
- elif type(address) in (Address, ScriptAddress):
- assert type(address) == Address
- script = address_to_script(address.address)
- if script_pub_key:
- assert script_pub_key == script
- else:
- script_pub_key = script
+ if n >= tx_in_count:
+ if self["format"] == "raw":
+ return b'\x01' + b'\x00' * 31
+ else:
+ return rh2s(b'\x01' + b'\x00' * 31)
- k = len(self["vIn"])
- self["vIn"][k] = dict()
- self["vIn"][k]["vOut"] = v_out
- self["vIn"][k]["sequence"] = sequence
- if self["format"] == "raw":
- self["vIn"][k]["txId"] = tx_id
- self["vIn"][k]["scriptSig"] = script_sig
- if script_pub_key:
- self["vIn"][k]["scriptPubKey"] = script_pub_key
+ # check script_pub_key for input
+ if script_pub_key is not None:
+ script_code = script_pub_key
+ else:
+ assert "scriptPubKey" in self["vIn"][n]
+ script_code = self["vIn"][n]["scriptPubKey"]
+ if type(script_code) == str:
+ script_code = unhexlify(script_code)
+ assert type(script_code) == bytes
+
+ # remove opcode separators
+ script_code = delete_from_script(script_code, BYTE_OPCODE["OP_CODESEPARATOR"])
+ preimage = bytearray()
+
+ if ((sighash_type & 31) == SIGHASH_SINGLE) and (n >= (len(self["vOut"]))):
+ if self["format"] == "raw":
+ return b'\x01' + b'\x00' * 31
+ else:
+ return rh2s(b'\x01' + b'\x00' * 31)
+
+ preimage += struct.pack(' n and (sighash_type & 31) == SIGHASH_SINGLE:
+ continue
+ if (sighash_type & 31) == SIGHASH_SINGLE and (n != i):
+ preimage += b'\xff' * 8 + b'\x00'
else:
- self["vIn"][k]["txId"] = rh2s(tx_id)
- self["vIn"][k]["scriptSig"] = hexlify(script_sig).decode()
- self["vIn"][k]["scriptSigOpcodes"] = decode_script(script_sig)
- self["vIn"][k]["scriptSigAsm"] = decode_script(script_sig, 1)
- if script_pub_key:
- self["vIn"][k]["scriptPubKey"] = hexlify(script_pub_key).decode()
- if tx_in_witness:
- self["segwit"] = True
- self["vIn"][k]["txInWitness"] = witness
- if amount:
- self["vIn"][k]["value"] = amount
- if private_key:
- self["vIn"][k].private_key = private_key
- self.__refresh__()
- return self
+ preimage += self["vOut"][i]["value"].to_bytes(8, 'little')
+ preimage += int_to_var_int(len(script_pub_key)) + script_pub_key
- def add_output(self, amount, address=None, script_pub_key=None):
- assert address is not None or script_pub_key is not None
- assert not (address is None and script_pub_key is None)
- assert type(amount) == int
- assert amount >= 0 and amount <= MAX_AMOUNT
- if script_pub_key:
- if type(script_pub_key) == str:
- script_pub_key = unhexlify(script_pub_key)
- assert type(script_pub_key) == bytes
- else:
- if type(address) == Address:
- address = address.address
- script_pub_key = address_to_script(address)
+ preimage += self["lockTime"].to_bytes(4, 'little')
+ preimage += struct.pack(b"= 0
+ tx_in_count = len(self["vIn"])
- if self["format"] == "raw":
- self["vOut"][k]["scriptPubKey"] = script_pub_key
- if self["data"] is None:
- if s["nType"] == 3:
- self["data"] = s["data"]
- if s["nType"] not in (3, 4, 7):
- self["vOut"][k]["addressHash"] = s["addressHash"]
- self["vOut"][k]["reqSigs"] = s["reqSigs"]
- else:
- self["vOut"][k]["scriptPubKey"] = hexlify(script_pub_key).decode()
- if self["data"] is None:
- if s["nType"] == 3:
- self["data"] = hexlify(s["data"]).decode()
- if s["nType"] not in (3, 4, 7):
- self["vOut"][k]["addressHash"] = hexlify(s["addressHash"]).decode()
- self["vOut"][k]["reqSigs"] = s["reqSigs"]
- self["vOut"][k]["scriptPubKeyOpcodes"] = decode_script(script_pub_key)
- self["vOut"][k]["scriptPubKeyAsm"] = decode_script(script_pub_key, 1)
- sh = True if self["vOut"][k]["nType"] in (1, 5) else False
- witness_version = None if self["vOut"][k]["nType"] < 5 else 0
- if "addressHash" in self["vOut"][k]:
- self["vOut"][k]["address"] = hash_to_address(self["vOut"][k]["addressHash"],
- self["testnet"],
- sh,
- witness_version)
- self.__refresh__()
- return self
+ if n >= tx_in_count:
+ if self["format"] == "raw":
+ return b'\x01' + b'\x00' * 31
+ else:
+ return rh2s(b'\x01' + b'\x00' * 31)
- def del_output(self, n=None):
- if not self["vOut"]:
- return self
- if n is None:
- n = len(self["vOut"]) - 1
- new_out = dict()
- c = 0
- for i in range(len(self["vOut"])):
- if i != n:
- new_out[c] = self["vOut"][i]
- c += 1
- self["vOut"] = new_out
- self.__refresh__()
- return self
+ # check script_pub_key for input
+ if script_pub_key is not None:
+ script_code = script_pub_key
+ else:
+ assert "scriptPubKey" in self["vIn"][n]
+ script_code = self["vIn"][n]["scriptPubKey"]
+ if type(script_code) == str:
+ script_code = unhexlify(script_code)
+ assert type(script_code) == bytes
- def del_input(self, n):
- if not self["vIn"]:
- return self
- if n is None:
- n = len(self["vIn"]) - 1
- new_in = dict()
- c = 0
- for i in range(len(self["vIn"])):
- if i != n:
- new_in[c] = self["vIn"][i]
- c += 1
- self["vIn"] = new_in
- self.__refresh__()
- return self
+ # remove opcode separators
+ preimage = bytearray()
+ # 1. nVersion of the transaction (4-byte little endian)
+ preimage += struct.pack('= 0
- tx_in_count = len(self["vIn"])
-
- if n >= tx_in_count:
+ def __refresh__(self):
+ if not self["vOut"] or not self["vIn"]:
+ return
+ if self["segwit"]:
+ for i in self["vIn"]:
+ if "txInWitness" not in self["vIn"][i]:
if self["format"] == "raw":
- return b'\x01' + b'\x00' * 31
+ self["vIn"][i]["txInWitness"] = []
else:
- return rh2s(b'\x01' + b'\x00' * 31)
+ self["vIn"][i]["txInWitness"] = []
+ no_segwit_view = self.serialize(segwit=False, hex=False)
+ self["txId"] = double_sha256(no_segwit_view)
+ self["rawTx"] = self.serialize(segwit=True, hex=False)
+ self["hash"] = double_sha256(self["rawTx"])
- # check script_pub_key for input
- if script_pub_key is not None:
- script_code = script_pub_key
- else:
- assert "scriptPubKey" in self["vIn"][n]
- script_code = self["vIn"][n]["scriptPubKey"]
- if type(script_code) == str:
- script_code = unhexlify(script_code)
- assert type(script_code) == bytes
+ self["size"] = len(self["rawTx"])
+ self["bSize"] = len(no_segwit_view)
+ self["weight"] = self["bSize"] * 3 + self["size"]
+ self["vSize"] = math.ceil(self["weight"] / 4)
- # remove opcode separators
- script_code = delete_from_script(script_code, BYTE_OPCODE["OP_CODESEPARATOR"])
- preimage = bytearray()
+ if self["format"] != "raw":
+ self["txId"] = rh2s(self["txId"])
+ self["hash"] = rh2s(self["hash"])
+ self["rawTx"] = hexlify(self["rawTx"]).decode()
- if ((sighash_type & 31) == SIGHASH_SINGLE) and (n >= (len(self["vOut"]))):
- if self["format"] == "raw":
- return b'\x01' + b'\x00' * 31
- else:
- return rh2s(b'\x01' + b'\x00' * 31)
+ input_sum = 0
+ for i in self["vIn"]:
+ if "value" in self["vIn"][i]:
+ input_sum += self["vIn"][i]["value"]
+ else:
+ input_sum = None
+ break
- preimage += struct.pack(' n and (sighash_type & 31) == SIGHASH_SINGLE:
- continue
- if (sighash_type & 31) == SIGHASH_SINGLE and (n != i):
- preimage += b'\xff' * 8 + b'\x00'
- else:
- preimage += self["vOut"][i]["value"].to_bytes(8, 'little')
- preimage += int_to_var_int(len(script_pub_key)) + script_pub_key
-
- preimage += self["lockTime"].to_bytes(4, 'little')
- preimage += struct.pack(b" |