github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/lib/btc/multisig.go (about)

     1  package btc
     2  
     3  import (
     4  	"fmt"
     5  	"bytes"
     6  	"errors"
     7  )
     8  
     9  type MultiSig struct {
    10  	SigsNeeded uint
    11  	Signatures []*Signature
    12  	PublicKeys [][]byte
    13  }
    14  
    15  
    16  func NewMultiSig(n uint) (res *MultiSig) {
    17  	res = new(MultiSig)
    18  	res.SigsNeeded = n
    19  	return
    20  }
    21  
    22  func NewMultiSigFromP2SH(p []byte) (*MultiSig, error) {
    23  	res := new(MultiSig)
    24  	er := res.ApplyP2SH(p)
    25  	if er != nil {
    26  		return nil, er
    27  	}
    28  	return res, nil
    29  }
    30  
    31  
    32  func NewMultiSigFromScript(p []byte) (*MultiSig, error) {
    33  	r := new(MultiSig)
    34  
    35  	var idx, stage int
    36  	for idx < len(p) {
    37  		opcode, pv, n, er := GetOpcode(p[idx:])
    38  		if er != nil {
    39  			return nil, errors.New("NewMultiSigFromScript: " + er.Error())
    40  		}
    41  		idx+= n
    42  
    43  		switch stage {
    44  			case 0: // look for OP_FALSE
    45  				if opcode!=0 {
    46  					return nil, errors.New("NewMultiSigFromScript: first opcode must be OP_0")
    47  				}
    48  				stage = 1
    49  
    50  			case 1: // look for signatures
    51  				sig, _ := NewSignature(pv)
    52  				if sig!=nil {
    53  					r.Signatures = append(r.Signatures, sig)
    54  					break
    55  				}
    56  				er := r.ApplyP2SH(pv)
    57  				if er != nil {
    58  					return nil, er
    59  				}
    60  				stage = 6
    61  
    62  			default:
    63  				return nil, errors.New(fmt.Sprintf("NewMultiSigFromScript: Unexpected opcode 0x%02X at the end of script", opcode))
    64  		}
    65  	}
    66  
    67  	if stage != 6 {
    68  		return nil, errors.New("NewMultiSigFromScript:  script too short")
    69  	}
    70  
    71  	return r, nil
    72  }
    73  
    74  
    75  func (r *MultiSig) ApplyP2SH(p []byte) (error) {
    76  	var idx, stage int
    77  	stage = 2
    78  	for idx < len(p) {
    79  		opcode, pv, n, er := GetOpcode(p[idx:])
    80  		if er != nil {
    81  			return errors.New("ApplyP2SH: " + er.Error())
    82  		}
    83  		idx+= n
    84  
    85  		switch stage {
    86  			case 2: // Look for number of required signatures
    87  				if opcode<OP_1 || opcode>OP_16 {
    88  					return errors.New(fmt.Sprint("ApplyP2SH: Unexpected number of required signatures ", opcode-OP_1+1))
    89  				}
    90  				r.SigsNeeded = uint(opcode-OP_1+1)
    91  				stage = 3
    92  
    93  			case 3: // Look for public keys
    94  				if len(pv)==33 && (pv[0]|1)==3 || len(pv)==65 && pv[0]==4 {
    95  					r.PublicKeys = append(r.PublicKeys, pv)
    96  					break
    97  				}
    98  				stage = 4
    99  				fallthrough
   100  
   101  			case 4: // Look for number of public keys
   102  				if opcode-OP_1+1 != len(r.PublicKeys) {
   103  					return errors.New(fmt.Sprint("ApplyP2SH: Number of public keys mismatch ", opcode-OP_1+1, "/", len(r.PublicKeys)))
   104  				}
   105  				stage = 5
   106  
   107  			case 5:
   108  				if opcode==OP_CHECKMULTISIG {
   109  					stage = 6
   110  				} else {
   111  					return errors.New(fmt.Sprintf("ApplyP2SH: Unexpected opcode 0x%02X at the end of script", opcode))
   112  				}
   113  		}
   114  	}
   115  
   116  	if stage != 6 {
   117  		return errors.New("ApplyP2SH:  script too short")
   118  	}
   119  
   120  	return nil
   121  }
   122  
   123  
   124  func (ms *MultiSig) P2SH() []byte {
   125  	buf := new(bytes.Buffer)
   126  	buf.WriteByte(byte(ms.SigsNeeded-1+OP_1))
   127  	for i := range ms.PublicKeys {
   128  		pk := ms.PublicKeys[i]
   129  		WriteVlen(buf, uint64(len(pk)))
   130  		buf.Write(pk)
   131  	}
   132  	buf.WriteByte(byte(len(ms.PublicKeys)-1+OP_1))
   133  	buf.WriteByte(OP_CHECKMULTISIG)
   134  	return buf.Bytes()
   135  }
   136  
   137  
   138  func (ms *MultiSig) Bytes() []byte {
   139  	buf := new(bytes.Buffer)
   140  	buf.WriteByte(OP_FALSE)
   141  	for i := range ms.Signatures {
   142  		sb := ms.Signatures[i].Bytes()
   143  		WriteVlen(buf, uint64(len(sb)))
   144  		buf.Write(sb)
   145  	}
   146  	p2sh := ms.P2SH()
   147  	WritePutLen(buf, uint32(len(p2sh)))
   148  	buf.Write(p2sh)
   149  	return buf.Bytes()
   150  }
   151  
   152  
   153  func (ms *MultiSig) PkScript() (pkscr []byte) {
   154  	pkscr = make([]byte, 23)
   155  	pkscr[0] = 0xa9
   156  	pkscr[1] = 20
   157  	RimpHash(ms.P2SH(), pkscr[2:22])
   158  	pkscr[22] = 0x87
   159  	return
   160  }
   161  
   162  func (ms *MultiSig) BtcAddr(testnet bool) *BtcAddr {
   163  	var h [20]byte
   164  	RimpHash(ms.P2SH(), h[:])
   165  	return NewAddrFromHash160(h[:], AddrVerScript(testnet))
   166  }