github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/wallet/multisig.go (about) 1 package main 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "github.com/piotrnar/gocoin/lib/btc" 7 "io/ioutil" 8 ) 9 10 const MultiToSignOut = "multi2sign.txt" 11 12 // make_p2sh adds P2SH pre-signing data into a raw tx. 13 func make_p2sh() { 14 tx := raw_tx_from_file(*rawtx) 15 if tx == nil { 16 fmt.Println("ERROR: Cannot decode the raw transaction") 17 return 18 } 19 20 d, er := hex.DecodeString(*p2sh) 21 if er != nil { 22 println("P2SH hex data:", er.Error()) 23 return 24 } 25 26 ms, er := btc.NewMultiSigFromP2SH(d) 27 if er != nil { 28 println("Decode P2SH:", er.Error()) 29 return 30 } 31 32 fmt.Println("The P2SH data points to address", ms.BtcAddr(testnet).String()) 33 34 sd := ms.Bytes() 35 36 for i := range tx.TxIn { 37 if *input < 0 || i == *input { 38 tx.TxIn[i].ScriptSig = sd 39 fmt.Println("Input number", i, " - hash to sign:", hex.EncodeToString(tx.SignatureHash(d, i, btc.SIGHASH_ALL))) 40 } 41 } 42 ioutil.WriteFile(MultiToSignOut, []byte(hex.EncodeToString(tx.Serialize())), 0666) 43 fmt.Println("Transaction with", len(tx.TxIn), "inputs ready for multi-signing, stored in", MultiToSignOut) 44 } 45 46 // multisig_reorder reorders signatures to meet order of the keys. 47 // Removes signatures made by the same keys. 48 // Removes exessive signatures (keeps transaction size down). 49 func multisig_reorder(tx *btc.Tx) (all_signed bool) { 50 all_signed = true 51 for i := range tx.TxIn { 52 ms, _ := btc.NewMultiSigFromScript(tx.TxIn[i].ScriptSig) 53 if ms == nil { 54 continue 55 } 56 hash := tx.SignatureHash(ms.P2SH(), i, btc.SIGHASH_ALL) 57 58 var sigs []*btc.Signature 59 for ki := range ms.PublicKeys { 60 var sig *btc.Signature 61 for si := range ms.Signatures { 62 if btc.EcdsaVerify(ms.PublicKeys[ki], ms.Signatures[si].Bytes(), hash) { 63 //fmt.Println("Key number", ki, "has signature number", si) 64 sig = ms.Signatures[si] 65 break 66 } 67 } 68 if sig != nil { 69 sigs = append(sigs, sig) 70 } else if *verbose { 71 fmt.Println("WARNING: Key number", ki, "has no matching signature") 72 } 73 74 if !*allowextramsigns && uint(len(sigs)) >= ms.SigsNeeded { 75 break 76 } 77 } 78 79 if *verbose { 80 if len(ms.Signatures) > len(sigs) { 81 fmt.Println("WARNING: Some signatures are obsolete and will be removed", len(ms.Signatures), "=>", len(sigs)) 82 } else if len(ms.Signatures) < len(sigs) { 83 fmt.Println("It appears that same key is re-used.", len(sigs)-len(ms.Signatures), "more signatures were added") 84 } 85 } 86 87 ms.Signatures = sigs 88 tx.TxIn[i].ScriptSig = ms.Bytes() 89 90 if len(sigs) < int(ms.SigsNeeded) { 91 all_signed = false 92 } 93 } 94 return 95 } 96 97 // multisig_sign signs a multisig transaction with a specific key. 98 func multisig_sign() { 99 tx := raw_tx_from_file(*rawtx) 100 if tx == nil { 101 println("ERROR: Cannot decode the raw multisig transaction") 102 println("Always use -msign <addr> along with -raw multi2sign.txt") 103 return 104 } 105 106 k := address_to_key(*multisign) 107 if k == nil { 108 println("You do not know a key for address", *multisign) 109 return 110 } 111 112 for i := range tx.TxIn { 113 ms, er := btc.NewMultiSigFromScript(tx.TxIn[i].ScriptSig) 114 if er != nil { 115 println("WARNING: Input", i, "- not multisig:", er.Error()) 116 continue 117 } 118 hash := tx.SignatureHash(ms.P2SH(), i, btc.SIGHASH_ALL) 119 //fmt.Println("Input number", i, len(ms.Signatures), " - hash to sign:", hex.EncodeToString(hash)) 120 121 r, s, e := btc.EcdsaSign(k.Key, hash) 122 if e != nil { 123 println(e.Error()) 124 return 125 } 126 btcsig := &btc.Signature{HashType: 0x01} 127 btcsig.R.Set(r) 128 btcsig.S.Set(s) 129 130 ms.Signatures = append(ms.Signatures, btcsig) 131 tx.TxIn[i].ScriptSig = ms.Bytes() 132 } 133 134 // Now re-order the signatures as they shall be: 135 multisig_reorder(tx) 136 137 write_tx_file(tx) 138 }