Completed
Pull Request — master (#518)
by thomas
58:22 queued 55:50
created

OutputScriptFactory   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 192
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 8

Test Coverage

Coverage 86.21%

Importance

Changes 0
Metric Value
dl 0
loc 192
ccs 50
cts 58
cp 0.8621
rs 10
c 0
b 0
f 0
wmc 27
lcom 0
cbo 8

14 Methods

Rating   Name   Duplication   Size   Complexity  
A p2pk() 0 4 1
A payToAddress() 0 6 2
A p2pkh() 0 4 1
A p2sh() 0 4 1
A p2wsh() 0 4 1
A p2wkh() 0 4 1
A payToPubKey() 0 4 1
A payToPubKeyHash() 0 8 2
A payToScriptHash() 0 8 2
A multisig() 0 6 1
C multisigKeyBuffers() 0 31 8
A witnessKeyHash() 0 8 2
A witnessScriptHash() 0 8 2
A witnessCoinbaseCommitment() 0 11 2
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
     */
22 16
    public function payToAddress(AddressInterface $address)
23
    {
24 16
        return $address instanceof ScriptHashAddress
25 4
            ? ScriptFactory::sequence([Opcodes::OP_HASH160, $address->getHash(), Opcodes::OP_EQUAL])
26 16
            : 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
     */
33 2
    public function p2pk(PublicKeyInterface $publicKey)
34
    {
35 2
        return $this->payToPubKey($publicKey);
36
    }
37
38
    /**
39
     * @param BufferInterface $pubKeyHash
40
     * @return ScriptInterface
41
     */
42 4
    public function p2pkh(BufferInterface $pubKeyHash)
43
    {
44 4
        return $this->payToPubKeyHash($pubKeyHash);
45
    }
46
47
    /**
48
     * @param BufferInterface $scriptHash
49
     * @return ScriptInterface
50
     */
51 24
    public function p2sh(BufferInterface $scriptHash)
52
    {
53 24
        return $this->payToScriptHash($scriptHash);
54
    }
55
56
    /**
57
     * @param BufferInterface $witnessScriptHash
58
     * @return ScriptInterface
59
     */
60 6
    public function p2wsh(BufferInterface $witnessScriptHash)
61
    {
62 6
        return $this->witnessScriptHash($witnessScriptHash);
63
    }
64
65
    /**
66
     * @param BufferInterface $witnessKeyHash
67
     * @return ScriptInterface
68
     */
69 2
    public function p2wkh(BufferInterface $witnessKeyHash)
70
    {
71 2
        return $this->witnessKeyHash($witnessKeyHash);
72
    }
73
    /**
74
     * Create a Pay to pubkey output
75
     *
76
     * @param PublicKeyInterface  $publicKey
77
     * @return ScriptInterface
78
     */
79 14
    public function payToPubKey(PublicKeyInterface $publicKey)
80
    {
81 14
        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
     */
90 48
    public function payToPubKeyHash(BufferInterface $pubKeyHash)
91
    {
92 48
        if ($pubKeyHash->getSize() !== 20) {
93
            throw new \RuntimeException('Public key hash must be exactly 20 bytes');
94
        }
95
96 48
        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
     */
106 50
    public function payToScriptHash(BufferInterface $scriptHash)
107
    {
108 50
        if ($scriptHash->getSize() !== 20) {
109
            throw new \RuntimeException('P2SH scriptHash must be exactly 20 bytes');
110
        }
111
112 50
        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
     */
121
    public function multisig($m, array $keys = [], $sort = true)
122
    {
123 26
        return self::multisigKeyBuffers($m, array_map(function (PublicKeyInterface $key) {
124 26
            return $key->getBuffer();
125 26
        }, $keys), $sort);
126
    }
127
128
    /**
129
     * @param int $m
130
     * @param BufferInterface[] $keys
131
     * @param bool|true $sort
132
     * @return ScriptInterface
133
     */
134 32
    public function multisigKeyBuffers($m, array $keys = [], $sort = true)
135
    {
136 32
        $n = count($keys);
137 32
        if ($m < 0) {
138
            throw new \LogicException('Number of signatures cannot be less than zero');
139
        }
140
141 32
        if ($m > $n) {
142
            throw new \LogicException('Required number of sigs exceeds number of public keys');
143
        }
144
145 32
        if ($n > 20) {
146
            throw new \LogicException('Number of public keys is greater than 16');
147
        }
148
149 32
        if ($sort) {
150 10
            $keys = Buffertools::sort($keys);
151
        }
152
153 32
        $new = ScriptFactory::create();
154 32
        $new->int($m);
155 32
        foreach ($keys as $key) {
156 32
            if ($key->getSize() != PublicKey::LENGTH_COMPRESSED && $key->getSize() != PublicKey::LENGTH_UNCOMPRESSED) {
157
                throw new \RuntimeException("Invalid length for public key buffer");
158
            }
159
160 32
            $new->push($key);
161
        }
162
163 32
        return $new->int($n)->op('OP_CHECKMULTISIG')->getScript();
164
    }
165
166
    /**
167
     * @param BufferInterface $keyHash
168
     * @return ScriptInterface
169
     */
170 2
    public function witnessKeyHash(BufferInterface $keyHash)
171
    {
172 2
        if ($keyHash->getSize() !== 20) {
173
            throw new \RuntimeException('witness key-hash should be 20 bytes');
174
        }
175
176 2
        return ScriptFactory::sequence([Opcodes::OP_0, $keyHash]);
177
    }
178
179
    /**
180
     * @param BufferInterface $scriptHash
181
     * @return ScriptInterface
182
     */
183 8
    public function witnessScriptHash(BufferInterface $scriptHash)
184
    {
185 8
        if ($scriptHash->getSize() !== 32) {
186
            throw new \RuntimeException('witness script-hash should be 32 bytes');
187
        }
188
189 8
        return ScriptFactory::sequence([Opcodes::OP_0, $scriptHash]);
190
    }
191
192
    /**
193
     * @param BufferInterface $commitment
194
     * @return ScriptInterface
195
     */
196 6
    public function witnessCoinbaseCommitment(BufferInterface $commitment)
197
    {
198 6
        if ($commitment->getSize() !== 32) {
199 2
            throw new \RuntimeException('Witness commitment hash must be exactly 32-bytes');
200
        }
201
202 4
        return ScriptFactory::sequence([
203 4
            Opcodes::OP_RETURN,
204 4
            new Buffer("\xaa\x21\xa9\xed" . $commitment->getBinary())
205
        ]);
206
    }
207
}
208