1 | <?php |
||
16 | class OutputScriptFactory |
||
17 | { |
||
18 | /** |
||
19 | * @param PublicKeyInterface $publicKey |
||
20 | * @return ScriptInterface |
||
21 | */ |
||
22 | public function p2pk(PublicKeyInterface $publicKey): ScriptInterface |
||
23 | { |
||
24 | 10 | return $this->payToPubKey($publicKey); |
|
25 | } |
||
26 | 10 | ||
27 | 4 | /** |
|
28 | 10 | * @param BufferInterface $pubKeyHash |
|
29 | * @return ScriptInterface |
||
30 | */ |
||
31 | public function p2pkh(BufferInterface $pubKeyHash): ScriptInterface |
||
32 | { |
||
33 | return $this->payToPubKeyHash($pubKeyHash); |
||
34 | } |
||
35 | 1 | ||
36 | /** |
||
37 | 1 | * @param BufferInterface $scriptHash |
|
38 | * @return ScriptInterface |
||
39 | */ |
||
40 | public function p2sh(BufferInterface $scriptHash): ScriptInterface |
||
41 | { |
||
42 | return $this->payToScriptHash($scriptHash); |
||
43 | } |
||
44 | 10 | ||
45 | /** |
||
46 | 10 | * @param BufferInterface $witnessScriptHash |
|
47 | * @return ScriptInterface |
||
48 | */ |
||
49 | public function p2wsh(BufferInterface $witnessScriptHash): ScriptInterface |
||
50 | { |
||
51 | return $this->witnessScriptHash($witnessScriptHash); |
||
52 | } |
||
53 | 59 | ||
54 | /** |
||
55 | 59 | * @param BufferInterface $witnessKeyHash |
|
56 | * @return ScriptInterface |
||
57 | */ |
||
58 | public function p2wkh(BufferInterface $witnessKeyHash): ScriptInterface |
||
59 | { |
||
60 | return $this->witnessKeyHash($witnessKeyHash); |
||
61 | } |
||
62 | 41 | /** |
|
63 | * Create a Pay to pubkey output |
||
64 | 41 | * |
|
65 | * @param PublicKeyInterface $publicKey |
||
66 | * @return ScriptInterface |
||
67 | */ |
||
68 | public function payToPubKey(PublicKeyInterface $publicKey): ScriptInterface |
||
69 | { |
||
70 | return ScriptFactory::sequence([$publicKey->getBuffer(), Opcodes::OP_CHECKSIG]); |
||
71 | 1 | } |
|
72 | |||
73 | 1 | /** |
|
74 | * Create a P2PKH output script |
||
75 | * |
||
76 | * @param BufferInterface $pubKeyHash |
||
77 | * @return ScriptInterface |
||
78 | */ |
||
79 | public function payToPubKeyHash(BufferInterface $pubKeyHash): ScriptInterface |
||
80 | { |
||
81 | 6 | if ($pubKeyHash->getSize() !== 20) { |
|
82 | throw new \RuntimeException('Public key hash must be exactly 20 bytes'); |
||
83 | 6 | } |
|
84 | |||
85 | return ScriptFactory::sequence([Opcodes::OP_DUP, Opcodes::OP_HASH160, $pubKeyHash, Opcodes::OP_EQUALVERIFY, Opcodes::OP_CHECKSIG]); |
||
86 | } |
||
87 | |||
88 | /** |
||
89 | /** |
||
90 | * Create a P2SH output script |
||
91 | * |
||
92 | 47 | * @param BufferInterface $scriptHash |
|
93 | * @return ScriptInterface |
||
94 | 47 | */ |
|
95 | public function payToScriptHash(BufferInterface $scriptHash): ScriptInterface |
||
96 | { |
||
97 | if ($scriptHash->getSize() !== 20) { |
||
98 | 47 | throw new \RuntimeException('P2SH scriptHash must be exactly 20 bytes'); |
|
99 | } |
||
100 | |||
101 | return ScriptFactory::sequence([Opcodes::OP_HASH160, $scriptHash, Opcodes::OP_EQUAL]); |
||
102 | } |
||
103 | |||
104 | /** |
||
105 | * @param int $m |
||
106 | * @param PublicKeyInterface[] $keys |
||
107 | * @param bool|true $sort |
||
108 | 76 | * @return ScriptInterface |
|
109 | */ |
||
110 | 76 | public function multisig(int $m, array $keys = [], bool $sort = true): ScriptInterface |
|
111 | { |
||
112 | return self::multisigKeyBuffers($m, array_map(function (PublicKeyInterface $key): BufferInterface { |
||
113 | return $key->getBuffer(); |
||
114 | 76 | }, $keys), $sort); |
|
115 | } |
||
116 | |||
117 | /** |
||
118 | * @param int $m |
||
119 | * @param BufferInterface[] $keys |
||
120 | * @param bool $sort |
||
121 | * @return ScriptInterface |
||
122 | */ |
||
123 | public function multisigKeyBuffers(int $m, array $keys = [], bool $sort = true): ScriptInterface |
||
124 | { |
||
125 | 18 | $n = count($keys); |
|
126 | 18 | if ($m < 0) { |
|
127 | 18 | throw new \LogicException('Number of signatures cannot be less than zero'); |
|
128 | } |
||
129 | |||
130 | if ($m > $n) { |
||
131 | throw new \LogicException('Required number of sigs exceeds number of public keys'); |
||
132 | } |
||
133 | |||
134 | if ($n > 20) { |
||
135 | throw new \LogicException('Number of public keys is greater than 16'); |
||
136 | 21 | } |
|
137 | |||
138 | 21 | if ($sort) { |
|
139 | 21 | $keys = Buffertools::sort($keys); |
|
140 | } |
||
141 | |||
142 | $new = ScriptFactory::create(); |
||
143 | 21 | $new->int($m); |
|
144 | foreach ($keys as $key) { |
||
145 | if ($key->getSize() !== PublicKey::LENGTH_COMPRESSED && $key->getSize() !== PublicKey::LENGTH_UNCOMPRESSED) { |
||
146 | throw new \RuntimeException("Invalid length for public key buffer"); |
||
147 | 21 | } |
|
148 | |||
149 | $new->push($key); |
||
150 | } |
||
151 | 21 | ||
152 | 5 | return $new->int($n)->op('OP_CHECKMULTISIG')->getScript(); |
|
153 | } |
||
154 | |||
155 | 21 | /** |
|
156 | 21 | * @param BufferInterface $keyHash |
|
157 | 21 | * @return ScriptInterface |
|
158 | 21 | */ |
|
159 | public function witnessKeyHash(BufferInterface $keyHash): ScriptInterface |
||
160 | { |
||
161 | if ($keyHash->getSize() !== 20) { |
||
162 | 21 | throw new \RuntimeException('witness key-hash should be 20 bytes'); |
|
163 | } |
||
164 | |||
165 | 21 | return ScriptFactory::sequence([Opcodes::OP_0, $keyHash]); |
|
166 | } |
||
167 | |||
168 | /** |
||
169 | * @param BufferInterface $scriptHash |
||
170 | * @return ScriptInterface |
||
171 | */ |
||
172 | 1 | public function witnessScriptHash(BufferInterface $scriptHash): ScriptInterface |
|
180 | |||
181 | /** |
||
182 | * @param BufferInterface $commitment |
||
183 | * @return ScriptInterface |
||
184 | */ |
||
185 | 42 | public function witnessCoinbaseCommitment(BufferInterface $commitment): ScriptInterface |
|
186 | { |
||
187 | 42 | if ($commitment->getSize() !== 32) { |
|
188 | throw new \RuntimeException('Witness commitment hash must be exactly 32-bytes'); |
||
189 | } |
||
190 | |||
191 | 42 | return ScriptFactory::sequence([ |
|
196 | } |
||
197 |