Completed
Pull Request — master (#514)
by thomas
72:28
created

OutputScriptFactory::multisigKeyBuffers()   C

Complexity

Conditions 8
Paths 9

Size

Total Lines 31
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 8.2327

Importance

Changes 0
Metric Value
cc 8
eloc 17
nc 9
nop 3
dl 0
loc 31
ccs 11
cts 13
cp 0.8462
crap 8.2327
rs 5.3846
c 0
b 0
f 0
1
<?php
2
3
namespace BitWasp\Bitcoin\Script\Factory;
4
5
use BitWasp\Bitcoin\Address\AddressInterface;
6
use BitWasp\Bitcoin\Address\ScriptHashAddress;
7
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Key\PublicKey;
8
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PublicKeyInterface;
9
use BitWasp\Bitcoin\Script\Opcodes;
10
use BitWasp\Bitcoin\Script\ScriptFactory;
11
use BitWasp\Bitcoin\Script\ScriptInterface;
12
use BitWasp\Buffertools\Buffer;
13
use BitWasp\Buffertools\BufferInterface;
14
use BitWasp\Buffertools\Buffertools;
15
16
class OutputScriptFactory
17
{
18
    /**
19
     * @param AddressInterface $address
20
     * @return ScriptInterface
21 10
     */
22
    public function payToAddress(AddressInterface $address)
23 10
    {
24 4
        return $address instanceof ScriptHashAddress
25 10
            ? ScriptFactory::sequence([Opcodes::OP_HASH160, $address->getHash(), Opcodes::OP_EQUAL])
26
            : ScriptFactory::sequence([Opcodes::OP_DUP, Opcodes::OP_HASH160, $address->getHash(), Opcodes::OP_EQUALVERIFY, Opcodes::OP_CHECKSIG]);
27
    }
28
29
    /**
30
     * @param PublicKeyInterface $publicKey
31
     * @return ScriptInterface
32 2
     */
33
    public function p2pk(PublicKeyInterface $publicKey)
34 2
    {
35
        return $this->payToPubKey($publicKey);
36
    }
37
38
    /**
39
     * @param BufferInterface $pubKeyHash
40
     * @return ScriptInterface
41 4
     */
42
    public function p2pkh(BufferInterface $pubKeyHash)
43 4
    {
44
        return $this->payToPubKeyHash($pubKeyHash);
45
    }
46
47
    /**
48
     * @param BufferInterface $scriptHash
49
     * @return ScriptInterface
50 24
     */
51
    public function p2sh(BufferInterface $scriptHash)
52 24
    {
53
        return $this->payToScriptHash($scriptHash);
54
    }
55
56
    /**
57
     * @param BufferInterface $witnessScriptHash
58
     * @return ScriptInterface
59 6
     */
60
    public function p2wsh(BufferInterface $witnessScriptHash)
61 6
    {
62
        return $this->witnessScriptHash($witnessScriptHash);
63
    }
64
65
    /**
66
     * @param BufferInterface $witnessKeyHash
67
     * @return ScriptInterface
68 2
     */
69
    public function p2wkh(BufferInterface $witnessKeyHash)
70 2
    {
71
        return $this->witnessKeyHash($witnessKeyHash);
72
    }
73
    /**
74
     * Create a Pay to pubkey output
75
     *
76
     * @param PublicKeyInterface  $publicKey
77
     * @return ScriptInterface
78 12
     */
79
    public function payToPubKey(PublicKeyInterface $publicKey)
80 12
    {
81
        return ScriptFactory::sequence([$publicKey->getBuffer(), Opcodes::OP_CHECKSIG]);
82
    }
83
84
    /**
85
     * Create a P2PKH output script
86
     *
87
     * @param BufferInterface $pubKeyHash
88
     * @return ScriptInterface
89 46
     */
90
    public function payToPubKeyHash(BufferInterface $pubKeyHash)
91 46
    {
92
        if ($pubKeyHash->getSize() !== 20) {
93
            throw new \RuntimeException('Public key hash must be exactly 20 bytes');
94
        }
95 46
96
        return ScriptFactory::sequence([Opcodes::OP_DUP, Opcodes::OP_HASH160, $pubKeyHash, Opcodes::OP_EQUALVERIFY, Opcodes::OP_CHECKSIG]);
97
    }
98
99
    /**
100
    /**
101
     * Create a P2SH output script
102
     *
103
     * @param BufferInterface $scriptHash
104
     * @return ScriptInterface
105 50
     */
106
    public function payToScriptHash(BufferInterface $scriptHash)
107 50
    {
108
        if ($scriptHash->getSize() !== 20) {
109
            throw new \RuntimeException('P2SH scriptHash must be exactly 20 bytes');
110
        }
111 50
112
        return ScriptFactory::sequence([Opcodes::OP_HASH160, $scriptHash, Opcodes::OP_EQUAL]);
113
    }
114
115
    /**
116
     * @param int $m
117
     * @param PublicKeyInterface[] $keys
118
     * @param bool|true $sort
119
     * @return ScriptInterface
120 26
     */
121
    public function multisig($m, array $keys = [], $sort = true)
122 26
    {
123 26
        return self::multisigKeyBuffers($m, array_map(function (PublicKeyInterface $key) {
124
            return $key->getBuffer();
125
        }, $keys), $sort);
126
    }
127 26
128
    /**
129
     * @param int $m
130
     * @param BufferInterface[] $keys
131 26
     * @param bool|true $sort
132
     * @return ScriptInterface
133
     */
134
    public function multisigKeyBuffers($m, array $keys = [], $sort = true)
135 26
    {
136 12
        $n = count($keys);
137
        if ($m < 0) {
138
            throw new \LogicException('Number of signatures cannot be less than zero');
139 26
        }
140 26
141 26
        if ($m > $n) {
142 26
            throw new \LogicException('Required number of sigs exceeds number of public keys');
143
        }
144
145
        if ($n > 20) {
146 26
            throw new \LogicException('Number of public keys is greater than 16');
147
        }
148
149 26
        if ($sort) {
150
            $keys = Buffertools::sort($keys);
151
        }
152
153
        $new = ScriptFactory::create();
154
        $new->int($m);
155
        foreach ($keys as $key) {
156 2
            if ($key->getSize() != PublicKey::LENGTH_COMPRESSED && $key->getSize() != PublicKey::LENGTH_UNCOMPRESSED) {
157
                throw new \RuntimeException("Invalid length for public key buffer");
158 2
            }
159
160
            $new->push($key);
161
        }
162 2
163
        return $new->int($n)->op('OP_CHECKMULTISIG')->getScript();
164
    }
165
166
    /**
167
     * @param BufferInterface $keyHash
168
     * @return ScriptInterface
169 8
     */
170
    public function witnessKeyHash(BufferInterface $keyHash)
171 8
    {
172
        if ($keyHash->getSize() !== 20) {
173
            throw new \RuntimeException('witness key-hash should be 20 bytes');
174
        }
175 8
176
        return ScriptFactory::sequence([Opcodes::OP_0, $keyHash]);
177
    }
178
179
    /**
180
     * @param BufferInterface $scriptHash
181
     * @return ScriptInterface
182 6
     */
183
    public function witnessScriptHash(BufferInterface $scriptHash)
184 6
    {
185 2
        if ($scriptHash->getSize() !== 32) {
186
            throw new \RuntimeException('witness script-hash should be 32 bytes');
187
        }
188 4
189 4
        return ScriptFactory::sequence([Opcodes::OP_0, $scriptHash]);
190 4
    }
191
192
    /**
193
     * @param BufferInterface $commitment
194
     * @return ScriptInterface
195
     */
196
    public function witnessCoinbaseCommitment(BufferInterface $commitment)
197
    {
198
        if ($commitment->getSize() !== 32) {
199
            throw new \RuntimeException('Witness commitment hash must be exactly 32-bytes');
200
        }
201
202
        return ScriptFactory::sequence([
203
            Opcodes::OP_RETURN,
204
            new Buffer("\xaa\x21\xa9\xed" . $commitment->getBinary())
205
        ]);
206
    }
207
}
208