Completed
Pull Request — master (#517)
by thomas
72:19
created

AddressFactory::fromString()   B

Complexity

Conditions 6
Paths 32

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 14
nc 32
nop 2
dl 0
loc 25
ccs 6
cts 6
cp 1
crap 6
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
namespace BitWasp\Bitcoin\Address;
4
5
use BitWasp\Bitcoin\Base58;
6
use BitWasp\Bitcoin\Bitcoin;
7
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\KeyInterface;
8
use BitWasp\Bitcoin\Key\PublicKeyFactory;
9
use BitWasp\Bitcoin\Network\NetworkInterface;
10
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier;
11
use BitWasp\Bitcoin\Script\ScriptInterface;
12
use BitWasp\Bitcoin\Script\ScriptType;
13
use BitWasp\Bitcoin\Script\WitnessProgram;
14
use BitWasp\Bitcoin\SegwitBech32;
15
use BitWasp\Buffertools\BufferInterface;
16
17
class AddressFactory
18
{
19
    /**
20
     * Returns a pay-to-pubkey-hash address for the given public key
21
     *
22
     * @param KeyInterface $key
23 44
     * @return PayToPubKeyHashAddress
24
     */
25 44
    public static function fromKey(KeyInterface $key)
26
    {
27
        return new PayToPubKeyHashAddress($key->getPubKeyHash());
28
    }
29
30
    /**
31
     * Takes the $p2shScript and generates the scriptHash address.
32
     *
33
     * @param ScriptInterface $p2shScript
34 12
     * @return ScriptHashAddress
35
     */
36 12
    public static function fromScript(ScriptInterface $p2shScript)
37
    {
38
        return new ScriptHashAddress($p2shScript->getScriptHash());
39
    }
40
41
    /**
42
     * @param WitnessProgram $wp
43 8
     * @return SegwitAddress
44
     */
45 8
    public static function fromWitnessProgram(WitnessProgram $wp)
46 8
    {
47 8
        return new SegwitAddress($wp);
48
    }
49 4
50 6
    /**
51
     * @param ScriptInterface $outputScript
52 2
     * @return AddressInterface
53
     */
54 4
    public static function fromOutputScript(ScriptInterface $outputScript)
55
    {
56
        $wp = null;
57
        if ($outputScript->isWitness($wp)) {
58
            return new SegwitAddress($wp);
0 ignored issues
show
Bug introduced by
It seems like $wp defined by null on line 56 can be null; however, BitWasp\Bitcoin\Address\...tAddress::__construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
59
        }
60
61
        $decode = (new OutputClassifier())->decode($outputScript);
62
        switch ($decode->getType()) {
63
            case ScriptType::P2PKH:
64 42
                /** @var BufferInterface $solution */
65
                return new PayToPubKeyHashAddress($decode->getSolution());
66 42
            case ScriptType::P2SH:
67 42
                /** @var BufferInterface $solution */
68 42
                return new ScriptHashAddress($decode->getSolution());
69
            default:
70 42
                throw new \RuntimeException('Script type is not associated with an address');
71 8
        }
72 34
    }
73 32
74
    /**
75 2
     * @param string $address
76
     * @param NetworkInterface $network
77
     * @return AddressInterface
78
     * @throws \BitWasp\Bitcoin\Exceptions\Base58ChecksumFailure
79
     */
80
    public static function fromString($address, NetworkInterface $network = null)
81
    {
82
        $network = $network ?: Bitcoin::getNetwork();
83
84
        try {
85 26
            $data = Base58::decodeCheck($address);
86
            $prefixByte = $data->slice(0, 1)->getHex();
87
88 26
            if ($prefixByte === $network->getP2shByte()) {
89 24
                return new ScriptHashAddress($data->slice(1));
90 2
            } else if ($prefixByte === $network->getAddressByte()) {
91 2
                return new PayToPubKeyHashAddress($data->slice(1));
92
            }
93
        } catch (\Exception $e) {
94 26
            // continue on for Bech32
95
        }
96
97
        try {
98
            return new SegwitAddress(SegwitBech32::decode($address, $network));
99
        } catch (\Exception $e) {
100
            // continue on
101
        }
102
103
        throw new \InvalidArgumentException("Address not recognized");
104
    }
105 4
106
    /**
107 4
     * @param string $address
108 4
     * @param NetworkInterface $network
109 4
     * @return bool
110 2
     * @throws \BitWasp\Bitcoin\Exceptions\Base58ChecksumFailure
111
     */
112 4
    public static function isValidAddress($address, NetworkInterface $network = null)
113
    {
114
        try {
115
            self::fromString($address, $network);
116
            $is_valid = true;
117
        } catch (\Exception $e) {
118
            $is_valid = false;
119
        }
120
121
        return $is_valid;
122
    }
123
124
    /**
125
     * Following a loose definition of 'associated', returns
126
     * the current script types, and a PayToPubKeyHash address for P2PK.
127
     *
128
     * @param ScriptInterface $script
129
     * @return AddressInterface
130
     * @throws \RuntimeException
131
     */
132
    public static function getAssociatedAddress(ScriptInterface $script)
133
    {
134
        $classifier = new OutputClassifier();
135
        $decode = $classifier->decode($script);
136
        if ($decode->getType() === ScriptType::P2PK) {
137
            return PublicKeyFactory::fromHex($decode->getSolution())->getAddress();
138
        } else {
139
            return self::fromOutputScript($script);
140
        }
141
    }
142
}
143