Completed
Push — master ( 332223...9023ba )
by thomas
34:12
created

ScriptCreator   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 157
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 86.15%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 157
ccs 56
cts 65
cp 0.8615
rs 10
wmc 22
lcom 1
cbo 9

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
C sequence() 0 24 7
A op() 0 6 1
B push() 0 30 4
B int() 0 12 5
A pushSerializable() 0 5 1
A concat() 0 5 1
A getScript() 0 4 1
1
<?php
2
3
namespace BitWasp\Bitcoin\Script\Factory;
4
5
use BitWasp\Bitcoin\Math\Math;
6
use BitWasp\Bitcoin\Script\Interpreter\Number;
7
use BitWasp\Bitcoin\Script\Opcodes;
8
use BitWasp\Bitcoin\Script\Script;
9
use BitWasp\Bitcoin\Script\ScriptInterface;
10
use BitWasp\Bitcoin\Serializable;
11
use BitWasp\Buffertools\Buffer;
12
use BitWasp\Buffertools\BufferInterface;
13
use BitWasp\Buffertools\Buffertools;
14
use BitWasp\Buffertools\Parser;
15
16
class ScriptCreator
17
{
18
    /**
19
     * @var string
20
     */
21
    private $script = '';
22
23
    /**
24
     * @var Opcodes
25
     */
26
    private $opcodes;
27
28
    /**
29
     * @var Math
30
     */
31
    private $math;
32
33
    /**
34
     * @param Math $math
35
     * @param Opcodes $opcodes
36
     * @param BufferInterface|null $buffer
37
     */
38 2428
    public function __construct(Math $math, Opcodes $opcodes, BufferInterface $buffer = null)
39
    {
40 2428
        if ($buffer !== null) {
41 42
            $this->script = $buffer->getBinary();
42 28
        }
43
44 2428
        $this->math = $math;
45 2428
        $this->opcodes = $opcodes;
46 2428
    }
47
48
    /**
49
     * @param int[]|\BitWasp\Bitcoin\Script\Interpreter\Number[]|BufferInterface[] $sequence
50
     * @return $this
51
     */
52 2338
    public function sequence(array $sequence)
53
    {
54 2338
        $new = new self($this->math, $this->opcodes, null);
55 2338
        foreach ($sequence as $operation) {
56 2338
            if (is_int($operation)) {
57 2332
                if (!$this->opcodes->offsetExists($operation)) {
58
                    throw new \RuntimeException('Unknown opcode');
59
                }
60
61 2332
                $new->script .= chr($operation);
62 1206
            } elseif ($operation instanceof Number) {
63
                $new->push($operation->getBuffer());
64 102
            } elseif ($operation instanceof BufferInterface) {
65 156
                $new->push($operation);
66 102
            } elseif ($operation instanceof ScriptInterface) {
67
                $new->concat($operation);
68
            } else {
69 1132
                throw new \RuntimeException('Value must be an opcode/BufferInterface/Number');
70
            }
71 1206
        }
72
73 2338
        $this->concat($new->getScript());
74 2338
        return $this;
75
    }
76
77
    /**
78
     * Add an opcode to the script
79
     *
80
     * @param string $name
81
     * @return $this
82
     */
83 87
    public function op($name)
84
    {
85 87
        $code = $this->opcodes->getOpByName($name);
86 84
        $this->script .= chr($code);
87 84
        return $this;
88
    }
89
90
    /**
91
     * Push data into the stack.
92
     *
93
     * @param $data
94
     * @return $this
95
     * @throws \Exception
96
     */
97 215
    public function push(BufferInterface $data)
98
    {
99 215
        $length = $data->getSize();
100 215
        $parsed = new Parser('', $this->math);
101
102
        /** Note that larger integers are serialized without flipping bits - Big endian */
103
104 215
        if ($length < $this->opcodes->getOpByName('OP_PUSHDATA1')) {
105 206
            $varInt = Buffertools::numToVarInt($length);
106 206
            $data = new Buffer($varInt->getBinary() . $data->getBinary(), null, $this->math);
107 206
            $parsed->writeBytes($data->getSize(), $data);
108 133
        } else {
109 12
            if ($length <= 0xff) {
110 6
                $lengthSize = 1;
111 10
            } elseif ($length <= 0xffff) {
112 3
                $lengthSize = 2;
113 2
            } else {
114 3
                $lengthSize = 4;
115
            }
116
117 12
            $op = $this->opcodes->getOpByName('OP_PUSHDATA' . $lengthSize);
118
            $parsed
119 12
                ->writeBytes(1, Buffer::int($op))
120 12
                ->writeBytes($lengthSize, Buffer::int($length), true)
121 12
                ->writeBytes($length, $data);
122
        }
123
124 215
        $this->script .= $parsed->getBuffer()->getBinary();
125 215
        return $this;
126
    }
127
128
    /**
129
     * @param int $n
130
     * @return $this
131
     */
132 39
    public function int($n)
133
    {
134 39
        if ($n === 0) {
135
            $this->script .= chr(Opcodes::OP_0);
136 39
        } else if ($n === -1 || ($n >= 1 && $n <= 16)) {
137 39
            $this->script .= chr(\BitWasp\Bitcoin\Script\encodeOpN($n));
138 26
        } else {
139
            $this->push(Number::int($n)->getBuffer());
140
        }
141
142 39
        return $this;
143
    }
144
145
    /**
146
     * @param Serializable $object
147
     * @return $this
148
     */
149
    public function pushSerializable(Serializable $object)
150
    {
151
        $this->push($object->getBuffer());
152
        return $this;
153
    }
154
155
    /**
156
     * @param ScriptInterface $script
157
     * @return $this
158
     */
159 2338
    public function concat(ScriptInterface $script)
160
    {
161 2338
        $this->script .= $script->getBinary();
162 2338
        return $this;
163
    }
164
165
    /**
166
     * @return ScriptInterface
167
     */
168 2425
    public function getScript()
169
    {
170 2425
        return new Script(new Buffer($this->script, null, $this->math), $this->opcodes);
171
    }
172
}
173