Completed
Pull Request — master (#758)
by thomas
22:04
created

OutputScriptFactory   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 187
Duplicated Lines 0 %

Test Coverage

Coverage 79.31%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 46
c 1
b 0
f 1
dl 0
loc 187
ccs 46
cts 58
cp 0.7931
rs 10
wmc 27

14 Methods

Rating   Name   Duplication   Size   Complexity  
B multisigKeyBuffers() 0 30 8
A witnessKeyHash() 0 7 2
A p2sh() 0 3 1
A witnessCoinbaseCommitment() 0 9 2
A p2pk() 0 3 1
A payToScriptHash() 0 7 2
A p2pkh() 0 3 1
A witnessScriptHash() 0 7 2
A p2wkh() 0 3 1
A p2wsh() 0 3 1
A multisig() 0 5 1
A payToPubKeyHash() 0 7 2
A payToPubKey() 0 3 1
A taproot() 0 6 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Bitcoin\Script\Factory;
6
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 PublicKeyInterface $publicKey
20
     * @return ScriptInterface
21
     */
22 5
    public function p2pk(PublicKeyInterface $publicKey): ScriptInterface
23
    {
24 5
        return $this->payToPubKey($publicKey);
25
    }
26
27
    /**
28
     * @param BufferInterface $pubKeyHash
29
     * @return ScriptInterface
30
     */
31 32
    public function p2pkh(BufferInterface $pubKeyHash): ScriptInterface
32
    {
33 32
        return $this->payToPubKeyHash($pubKeyHash);
34
    }
35
36
    /**
37
     * @param BufferInterface $scriptHash
38
     * @return ScriptInterface
39
     */
40 73
    public function p2sh(BufferInterface $scriptHash): ScriptInterface
41
    {
42 73
        return $this->payToScriptHash($scriptHash);
43
    }
44
45
    /**
46
     * @param BufferInterface $witnessScriptHash
47
     * @return ScriptInterface
48
     */
49 57
    public function p2wsh(BufferInterface $witnessScriptHash): ScriptInterface
50
    {
51 57
        return $this->witnessScriptHash($witnessScriptHash);
52
    }
53
54
    /**
55
     * @param BufferInterface $witnessKeyHash
56
     * @return ScriptInterface
57
     */
58 13
    public function p2wkh(BufferInterface $witnessKeyHash): ScriptInterface
59
    {
60 13
        return $this->witnessKeyHash($witnessKeyHash);
61
    }
62
    /**
63
     * Create a Pay to pubkey output
64
     *
65
     * @param PublicKeyInterface  $publicKey
66
     * @return ScriptInterface
67
     */
68 9
    public function payToPubKey(PublicKeyInterface $publicKey): ScriptInterface
69
    {
70 9
        return ScriptFactory::sequence([$publicKey->getBuffer(), Opcodes::OP_CHECKSIG]);
71
    }
72
73
    /**
74
     * Create a P2PKH output script
75
     *
76
     * @param BufferInterface $pubKeyHash
77
     * @return ScriptInterface
78
     */
79 74
    public function payToPubKeyHash(BufferInterface $pubKeyHash): ScriptInterface
80
    {
81 74
        if ($pubKeyHash->getSize() !== 20) {
82
            throw new \RuntimeException('Public key hash must be exactly 20 bytes');
83
        }
84
85 74
        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
     * @param BufferInterface $scriptHash
93
     * @return ScriptInterface
94
     */
95 92
    public function payToScriptHash(BufferInterface $scriptHash): ScriptInterface
96
    {
97 92
        if ($scriptHash->getSize() !== 20) {
98
            throw new \RuntimeException('P2SH scriptHash must be exactly 20 bytes');
99
        }
100
101 92
        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
     * @return ScriptInterface
109
     */
110
    public function multisig(int $m, array $keys = [], bool $sort = true): ScriptInterface
111
    {
112 8
        return self::multisigKeyBuffers($m, array_map(function (PublicKeyInterface $key): BufferInterface {
0 ignored issues
show
Bug Best Practice introduced by
The method BitWasp\Bitcoin\Script\F...y::multisigKeyBuffers() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

112
        return self::/** @scrutinizer ignore-call */ multisigKeyBuffers($m, array_map(function (PublicKeyInterface $key): BufferInterface {
Loading history...
113 8
            return $key->getBuffer();
114 8
        }, $keys), $sort);
115
    }
116
117
    /**
118
     * @param int $m
119
     * @param BufferInterface[] $keys
120
     * @param bool $sort
121
     * @return ScriptInterface
122
     */
123 42
    public function multisigKeyBuffers(int $m, array $keys = [], bool $sort = true): ScriptInterface
124
    {
125 42
        $n = count($keys);
126 42
        if ($m < 0) {
127
            throw new \LogicException('Number of signatures cannot be less than zero');
128
        }
129
130 42
        if ($m > $n) {
131
            throw new \LogicException('Required number of sigs exceeds number of public keys');
132
        }
133
134 42
        if ($n > 20) {
135
            throw new \LogicException('Number of public keys is greater than 16');
136
        }
137
138 42
        if ($sort) {
139 29
            $keys = Buffertools::sort($keys);
140
        }
141
142 42
        $new = ScriptFactory::create();
143 42
        $new->int($m);
144 42
        foreach ($keys as $key) {
145 42
            if ($key->getSize() !== PublicKey::LENGTH_COMPRESSED && $key->getSize() !== PublicKey::LENGTH_UNCOMPRESSED) {
146
                throw new \RuntimeException("Invalid length for public key buffer");
147
            }
148
149 42
            $new->push($key);
150
        }
151
152 42
        return $new->int($n)->opcode(Opcodes::OP_CHECKMULTISIG)->getScript();
153
    }
154
155
    /**
156
     * @param BufferInterface $keyHash
157
     * @return ScriptInterface
158
     */
159 13
    public function witnessKeyHash(BufferInterface $keyHash): ScriptInterface
160
    {
161 13
        if ($keyHash->getSize() !== 20) {
162
            throw new \RuntimeException('witness key-hash should be 20 bytes');
163
        }
164
165 13
        return ScriptFactory::sequence([Opcodes::OP_0, $keyHash]);
166
    }
167
168
    /**
169
     * @param BufferInterface $scriptHash
170
     * @return ScriptInterface
171
     */
172 58
    public function witnessScriptHash(BufferInterface $scriptHash): ScriptInterface
173
    {
174 58
        if ($scriptHash->getSize() !== 32) {
175
            throw new \RuntimeException('witness script-hash should be 32 bytes');
176
        }
177
178 58
        return ScriptFactory::sequence([Opcodes::OP_0, $scriptHash]);
179
    }
180
181
    /**
182
     * @param BufferInterface $commitment
183
     * @return ScriptInterface
184
     */
185 3
    public function witnessCoinbaseCommitment(BufferInterface $commitment): ScriptInterface
186
    {
187 3
        if ($commitment->getSize() !== 32) {
188 1
            throw new \RuntimeException('Witness commitment hash must be exactly 32-bytes');
189
        }
190
191 2
        return ScriptFactory::sequence([
192 2
            Opcodes::OP_RETURN,
193 2
            new Buffer("\xaa\x21\xa9\xed" . $commitment->getBinary())
194
        ]);
195
    }
196
197
    public function taproot(BufferInterface $key32): ScriptInterface
198
    {
199
        if ($key32->getSize() !== 32) {
200
            throw new \RuntimeException('Taproot key should be 32 bytes');
201
        }
202
        return ScriptFactory::sequence([Opcodes::OP_1, $key32]);
203
    }
204
}
205