func dump_raw_sigscript(d []byte) bool { ss, er := btc.ScriptToText(d) if er != nil { println(er.Error()) return false } p2sh := len(ss) >= 2 && d[0] == 0 if p2sh { ms, er := btc.NewMultiSigFromScript(d) if er == nil { fmt.Println(" Multisig script", ms.SigsNeeded, "of", len(ms.PublicKeys)) for i := range ms.PublicKeys { fmt.Printf(" pkey%d = %s\n", i+1, hex.EncodeToString(ms.PublicKeys[i])) } for i := range ms.Signatures { fmt.Printf(" R%d = %64s\n", i+1, hex.EncodeToString(ms.Signatures[i].R.Bytes())) fmt.Printf(" S%d = %64s\n", i+1, hex.EncodeToString(ms.Signatures[i].S.Bytes())) fmt.Printf(" HashType%d = %02x\n", i+1, ms.Signatures[i].HashType) } return len(ms.Signatures) >= int(ms.SigsNeeded) } else { println(er.Error()) } } fmt.Println(" SigScript:", p2sh) for i := range ss { if p2sh && i == len(ss)-1 { // Print p2sh script d, _ = hex.DecodeString(ss[i]) s2, er := btc.ScriptToText(d) if er != nil { println(er.Error()) p2sh = false fmt.Println(" ", ss[i]) continue //return } fmt.Println(" P2SH spend script:") for j := range s2 { fmt.Println(" ", s2[j]) } } else { fmt.Println(" ", ss[i]) } } return true }
// reorder signatures to meet order of the keys // remove signatuers made by the same keys // remove exessive signatures (keeps transaction size down) func multisig_reorder(tx *btc.Tx) (all_signed bool) { all_signed = true for i := range tx.TxIn { ms, _ := btc.NewMultiSigFromScript(tx.TxIn[i].ScriptSig) if ms == nil { continue } hash := tx.SignatureHash(ms.P2SH(), i, btc.SIGHASH_ALL) var sigs []*btc.Signature for ki := range ms.PublicKeys { var sig *btc.Signature for si := range ms.Signatures { if btc.EcdsaVerify(ms.PublicKeys[ki], ms.Signatures[si].Bytes(), hash) { //fmt.Println("Key number", ki, "has signature number", si) sig = ms.Signatures[si] break } } if sig != nil { sigs = append(sigs, sig) } else if *verbose { fmt.Println("WARNING: Key number", ki, "has no matching signature") } if !*allowextramsigns && uint(len(sigs)) >= ms.SigsNeeded { break } } if *verbose { if len(ms.Signatures) > len(sigs) { fmt.Println("WARNING: Some signatures are obsolete and will be removed", len(ms.Signatures), "=>", len(sigs)) } else if len(ms.Signatures) < len(sigs) { fmt.Println("It appears that same key is re-used.", len(sigs)-len(ms.Signatures), "more signatures were added") } } ms.Signatures = sigs tx.TxIn[i].ScriptSig = ms.Bytes() if len(sigs) < int(ms.SigsNeeded) { all_signed = false } } return }
// sign a multisig transaction with a specific key func multisig_sign() { tx := raw_tx_from_file(*rawtx) if tx == nil { println("ERROR: Cannot decode the raw multisig transaction") println("Always use -msign <addr> along with -raw multi2sign.txt") return } k := address_to_key(*multisign) if k == nil { println("You do not know a key for address", *multisign) return } for i := range tx.TxIn { ms, er := btc.NewMultiSigFromScript(tx.TxIn[i].ScriptSig) if er != nil { println("WARNING: Input", i, "- not multisig:", er.Error()) continue } hash := tx.SignatureHash(ms.P2SH(), i, btc.SIGHASH_ALL) //fmt.Println("Input number", i, len(ms.Signatures), " - hash to sign:", hex.EncodeToString(hash)) r, s, e := btc.EcdsaSign(k.Key, hash) if e != nil { println(e.Error()) return } btcsig := &btc.Signature{HashType: 0x01} btcsig.R.Set(r) btcsig.S.Set(s) ms.Signatures = append(ms.Signatures, btcsig) tx.TxIn[i].ScriptSig = ms.Bytes() } // Now re-order the signatures as they shall be: multisig_reorder(tx) write_tx_file(tx) }
// prepare a signed transaction func sign_tx(tx *btc.Tx) (all_signed bool) { var multisig_done bool all_signed = true // go through each input for in := range tx.TxIn { if ms, _ := btc.NewMultiSigFromScript(tx.TxIn[in].ScriptSig); ms != nil { hash := tx.SignatureHash(ms.P2SH(), in, btc.SIGHASH_ALL) for ki := range ms.PublicKeys { k := public_to_key(ms.PublicKeys[ki]) if k != nil { r, s, e := btc.EcdsaSign(k.Key, hash) if e != nil { println("ERROR in sign_tx:", e.Error()) all_signed = false } else { btcsig := &btc.Signature{HashType: 0x01} btcsig.R.Set(r) btcsig.S.Set(s) ms.Signatures = append(ms.Signatures, btcsig) tx.TxIn[in].ScriptSig = ms.Bytes() multisig_done = true } } } } else { uo := getUO(&tx.TxIn[in].Input) if uo == nil { println("ERROR: Unkown input:", tx.TxIn[in].Input.String(), "- missing balance folder?") all_signed = false continue } adr := addr_from_pkscr(uo.Pk_script) if adr == nil { fmt.Println("WARNING: Don't know how to sign input number", in) fmt.Println(" Pk_script:", hex.EncodeToString(uo.Pk_script)) all_signed = false continue } k := hash_to_key(adr.Hash160) if k == nil { fmt.Println("WARNING: You do not have key for", adr.String(), "at input", in) all_signed = false continue } er := tx.Sign(in, uo.Pk_script, btc.SIGHASH_ALL, k.BtcAddr.Pubkey, k.Key) if er != nil { fmt.Println("ERROR: Sign failed for input number", in, er.Error()) all_signed = false } } } // reorder signatures if we signed any multisig inputs if multisig_done && !multisig_reorder(tx) { all_signed = false } if !all_signed { fmt.Println("WARNING: Not all the inputs have been signed") } return }