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  }