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 }