mirror of
https://github.com/haskoin/haskoin-core.git
synced 2024-09-11 10:05:34 +03:00
Support bech32 addresses with versions other than 0
This commit is contained in:
parent
6f120c7bda
commit
ee44a7e5f7
@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## 0.18.0
|
||||
### Added
|
||||
- Support SegWit addresses with version other than 0.
|
||||
|
||||
## 0.17.6
|
||||
### Added
|
||||
- Serialize instances for `XPubKey` and `XPrvKey`.
|
||||
|
@ -4,10 +4,10 @@ cabal-version: 1.12
|
||||
--
|
||||
-- see: https://github.com/sol/hpack
|
||||
--
|
||||
-- hash: 80ca7faee9e1700b5927a262126a774fb45156219675eb255ba45e31aabcfe92
|
||||
-- hash: 9861fe70166471990a6ee8fcf42174a9d40cda5b4e28119073d56a221e78fee3
|
||||
|
||||
name: haskoin-core
|
||||
version: 0.17.6
|
||||
version: 0.18.0
|
||||
synopsis: Bitcoin & Bitcoin Cash library for Haskell
|
||||
description: Please see the README on GitHub at <https://github.com/haskoin/haskoin-core#readme>
|
||||
category: Bitcoin, Finance, Network
|
||||
|
@ -1,5 +1,5 @@
|
||||
name: haskoin-core
|
||||
version: 0.17.6
|
||||
version: 0.18.0
|
||||
synopsis: Bitcoin & Bitcoin Cash library for Haskell
|
||||
description: Please see the README on GitHub at <https://github.com/haskoin/haskoin-core#readme>
|
||||
category: Bitcoin, Finance, Network
|
||||
|
@ -64,6 +64,7 @@ import Data.Hashable
|
||||
import Data.Maybe
|
||||
import Data.Serialize as S
|
||||
import Data.Text (Text)
|
||||
import Data.Word (Word8)
|
||||
import GHC.Generics (Generic)
|
||||
import Haskoin.Address.Base58
|
||||
import Haskoin.Address.Bech32
|
||||
@ -96,6 +97,11 @@ data Address
|
||||
{ getAddrHash256 :: !Hash256
|
||||
-- ^ HASH256 hash of script
|
||||
}
|
||||
-- | other witness address
|
||||
| WitnessAddress
|
||||
{ getAddrVersion :: !Word8
|
||||
, getAddrData :: !ByteString
|
||||
}
|
||||
deriving (Eq, Ord, Generic, Show, Read, Serialize, Hashable, NFData)
|
||||
|
||||
-- | 'Address' pays to a public key hash.
|
||||
@ -118,6 +124,10 @@ isWitnessScriptAddress :: Address -> Bool
|
||||
isWitnessScriptAddress WitnessScriptAddress {} = True
|
||||
isWitnessScriptAddress _ = False
|
||||
|
||||
isWitnessAddress :: Address -> Bool
|
||||
isWitnessAddress WitnessAddress {} = True
|
||||
isWitnessAddress _ = False
|
||||
|
||||
addrToJSON :: Network -> Address -> Value
|
||||
addrToJSON net a = toJSON (addrToText net a)
|
||||
|
||||
@ -151,6 +161,9 @@ addrToText net WitnessPubKeyAddress {getAddrHash160 = h} = do
|
||||
addrToText net WitnessScriptAddress {getAddrHash256 = h} = do
|
||||
hrp <- getBech32Prefix net
|
||||
segwitEncode hrp 0 (B.unpack (S.encode h))
|
||||
addrToText net WitnessAddress {getAddrVersion = v, getAddrData = d} = do
|
||||
hrp <- getBech32Prefix net
|
||||
segwitEncode hrp v (B.unpack d)
|
||||
|
||||
-- | Parse 'Base58', 'Bech32' or 'CashAddr' address, depending on network.
|
||||
textToAddr :: Network -> Text -> Maybe Address
|
||||
@ -169,11 +182,12 @@ bech32ToAddr :: Network -> Text -> Maybe Address
|
||||
bech32ToAddr net txt = do
|
||||
hrp <- getBech32Prefix net
|
||||
(ver, bs) <- second B.pack <$> segwitDecode hrp txt
|
||||
guard (ver == 0) -- We only support version 0 for now
|
||||
case B.length bs of
|
||||
20 -> WitnessPubKeyAddress <$> eitherToMaybe (S.decode bs)
|
||||
32 -> WitnessScriptAddress <$> eitherToMaybe (S.decode bs)
|
||||
_ -> Nothing
|
||||
case ver of
|
||||
0 -> case B.length bs of
|
||||
20 -> WitnessPubKeyAddress <$> eitherToMaybe (S.decode bs)
|
||||
32 -> WitnessScriptAddress <$> eitherToMaybe (S.decode bs)
|
||||
_ -> Nothing
|
||||
_ -> Just $ WitnessAddress ver bs
|
||||
|
||||
base58ToAddr :: Network -> Text -> Maybe Address
|
||||
base58ToAddr net txt =
|
||||
@ -250,10 +264,11 @@ payToNestedScriptAddress =
|
||||
addressToOutput :: Address -> ScriptOutput
|
||||
addressToOutput =
|
||||
\case
|
||||
(PubKeyAddress h) -> PayPKHash h
|
||||
(ScriptAddress h) -> PayScriptHash h
|
||||
(WitnessPubKeyAddress h) -> PayWitnessPKHash h
|
||||
(WitnessScriptAddress h) -> PayWitnessScriptHash h
|
||||
PubKeyAddress h -> PayPKHash h
|
||||
ScriptAddress h -> PayScriptHash h
|
||||
WitnessPubKeyAddress h -> PayWitnessPKHash h
|
||||
WitnessScriptAddress h -> PayWitnessScriptHash h
|
||||
WitnessAddress v d -> PayWitness v d
|
||||
|
||||
-- | Get output script AST for an 'Address'.
|
||||
addressToScript :: Address -> Script
|
||||
@ -277,11 +292,12 @@ scriptToAddressBS =
|
||||
outputAddress :: ScriptOutput -> Maybe Address
|
||||
outputAddress =
|
||||
\case
|
||||
(PayPKHash h) -> Just $ PubKeyAddress h
|
||||
(PayScriptHash h) -> Just $ ScriptAddress h
|
||||
(PayPK k) -> Just $ pubKeyAddr k
|
||||
(PayWitnessPKHash h) -> Just $ WitnessPubKeyAddress h
|
||||
(PayWitnessScriptHash h) -> Just $ WitnessScriptAddress h
|
||||
PayPKHash h -> Just $ PubKeyAddress h
|
||||
PayScriptHash h -> Just $ ScriptAddress h
|
||||
PayPK k -> Just $ pubKeyAddr k
|
||||
PayWitnessPKHash h -> Just $ WitnessPubKeyAddress h
|
||||
PayWitnessScriptHash h -> Just $ WitnessScriptAddress h
|
||||
PayWitness v d -> Just $ WitnessAddress v d
|
||||
_ -> Nothing
|
||||
|
||||
-- | Infer the 'Address' of a 'ScriptInput'.
|
||||
|
@ -53,7 +53,9 @@ import qualified Data.ByteString as BS
|
||||
import Data.Function (on)
|
||||
import Data.Hashable
|
||||
import Data.List (sortBy)
|
||||
import Data.Maybe (fromJust, isJust)
|
||||
import Data.Serialize as S
|
||||
import Data.Word (Word8)
|
||||
import GHC.Generics (Generic)
|
||||
import Haskoin.Constants
|
||||
import Haskoin.Crypto
|
||||
@ -79,6 +81,10 @@ data ScriptOutput
|
||||
| PayWitnessPKHash { getOutputHash :: !Hash160 }
|
||||
-- | pay to witness script hash
|
||||
| PayWitnessScriptHash { getScriptHash :: !Hash256 }
|
||||
-- | another pay to witness address
|
||||
| PayWitness { getWitnessVersion :: !Word8
|
||||
, getWitnessData :: !ByteString
|
||||
}
|
||||
-- | provably unspendable data carrier
|
||||
| DataCarrier { getOutputData :: !ByteString }
|
||||
deriving (Eq, Show, Read, Generic, Hashable, NFData)
|
||||
@ -124,6 +130,11 @@ isPayWitnessScriptHash :: ScriptOutput -> Bool
|
||||
isPayWitnessScriptHash (PayWitnessScriptHash _) = True
|
||||
isPayWitnessScriptHash _ = False
|
||||
|
||||
-- | Is script paying to a different type of witness address?
|
||||
isPayWitness :: ScriptOutput -> Bool
|
||||
isPayWitness (PayWitness _ _) = True
|
||||
isPayWitness _ = False
|
||||
|
||||
-- | Is script a data carrier output?
|
||||
isDataCarrier :: ScriptOutput -> Bool
|
||||
isDataCarrier (DataCarrier _) = True
|
||||
@ -145,11 +156,57 @@ decodeOutput s = case scriptOps s of
|
||||
[OP_0, OP_PUSHDATA bs OPCODE]
|
||||
| BS.length bs == 20 -> PayWitnessPKHash <$> S.decode bs
|
||||
| BS.length bs == 32 -> PayWitnessScriptHash <$> S.decode bs
|
||||
-- Other Witness
|
||||
[ver, OP_PUSHDATA bs _]
|
||||
| isJust (opWitnessVersion ver)
|
||||
&& BS.length bs >= 2
|
||||
&& BS.length bs <= 40 ->
|
||||
Right $ PayWitness (fromJust (opWitnessVersion ver)) bs
|
||||
-- Provably unspendable data carrier output
|
||||
[OP_RETURN, OP_PUSHDATA bs _] -> Right $ DataCarrier bs
|
||||
-- Pay to MultiSig Keys
|
||||
_ -> matchPayMulSig s
|
||||
|
||||
witnessVersionOp :: Word8 -> Maybe ScriptOp
|
||||
witnessVersionOp 0 = Just OP_0
|
||||
witnessVersionOp 1 = Just OP_1
|
||||
witnessVersionOp 2 = Just OP_2
|
||||
witnessVersionOp 3 = Just OP_3
|
||||
witnessVersionOp 4 = Just OP_4
|
||||
witnessVersionOp 5 = Just OP_5
|
||||
witnessVersionOp 6 = Just OP_6
|
||||
witnessVersionOp 7 = Just OP_7
|
||||
witnessVersionOp 8 = Just OP_8
|
||||
witnessVersionOp 9 = Just OP_9
|
||||
witnessVersionOp 10 = Just OP_10
|
||||
witnessVersionOp 11 = Just OP_11
|
||||
witnessVersionOp 12 = Just OP_12
|
||||
witnessVersionOp 13 = Just OP_13
|
||||
witnessVersionOp 14 = Just OP_14
|
||||
witnessVersionOp 15 = Just OP_15
|
||||
witnessVersionOp 16 = Just OP_16
|
||||
|
||||
opWitnessVersion :: ScriptOp -> Maybe Word8
|
||||
opWitnessVersion OP_0 = Just 0
|
||||
opWitnessVersion OP_1 = Just 1
|
||||
opWitnessVersion OP_2 = Just 2
|
||||
opWitnessVersion OP_3 = Just 3
|
||||
opWitnessVersion OP_4 = Just 4
|
||||
opWitnessVersion OP_5 = Just 5
|
||||
opWitnessVersion OP_6 = Just 6
|
||||
opWitnessVersion OP_7 = Just 7
|
||||
opWitnessVersion OP_8 = Just 8
|
||||
opWitnessVersion OP_9 = Just 9
|
||||
opWitnessVersion OP_10 = Just 10
|
||||
opWitnessVersion OP_11 = Just 11
|
||||
opWitnessVersion OP_12 = Just 12
|
||||
opWitnessVersion OP_13 = Just 13
|
||||
opWitnessVersion OP_14 = Just 14
|
||||
opWitnessVersion OP_15 = Just 15
|
||||
opWitnessVersion OP_16 = Just 16
|
||||
opWitnessVersion _ = Nothing
|
||||
|
||||
|
||||
-- | Similar to 'decodeOutput' but decodes from a 'ByteString'.
|
||||
decodeOutputBS :: ByteString -> Either String ScriptOutput
|
||||
decodeOutputBS = decodeOutput <=< S.decode
|
||||
@ -161,7 +218,11 @@ encodeOutput s = Script $ case s of
|
||||
(PayPK k) -> [opPushData $ S.encode k, OP_CHECKSIG]
|
||||
-- Pay to PubKey Hash Address
|
||||
(PayPKHash h) ->
|
||||
[ OP_DUP, OP_HASH160, opPushData $ S.encode h, OP_EQUALVERIFY, OP_CHECKSIG]
|
||||
[ OP_DUP
|
||||
, OP_HASH160
|
||||
, opPushData $ S.encode h
|
||||
, OP_EQUALVERIFY, OP_CHECKSIG
|
||||
]
|
||||
-- Pay to MultiSig Keys
|
||||
(PayMulSig ps r)
|
||||
| r <= length ps ->
|
||||
@ -178,6 +239,11 @@ encodeOutput s = Script $ case s of
|
||||
[ OP_0, opPushData $ S.encode h ]
|
||||
(PayWitnessScriptHash h) ->
|
||||
[ OP_0, opPushData $ S.encode h ]
|
||||
(PayWitness v h) ->
|
||||
[ case witnessVersionOp v of
|
||||
Nothing -> error "encodeOutput: invalid witness version"
|
||||
Just c -> c
|
||||
, opPushData h ]
|
||||
-- Provably unspendable output
|
||||
(DataCarrier d) -> [OP_RETURN, opPushData d]
|
||||
|
||||
|
@ -9,6 +9,7 @@ Portability : POSIX
|
||||
-}
|
||||
module Haskoin.Util.Arbitrary.Address where
|
||||
|
||||
import qualified Data.ByteString as B
|
||||
import Haskoin.Address
|
||||
import Haskoin.Constants
|
||||
import Haskoin.Util.Arbitrary.Crypto
|
||||
@ -26,6 +27,7 @@ arbitraryAddressAll =
|
||||
, arbitraryScriptAddress
|
||||
, arbitraryWitnessPubKeyAddress
|
||||
, arbitraryWitnessScriptAddress
|
||||
, arbitraryWitnessAddress
|
||||
]
|
||||
|
||||
-- | Arbitrary valid combination of (Network, Address)
|
||||
@ -51,3 +53,11 @@ arbitraryWitnessPubKeyAddress = WitnessPubKeyAddress <$> arbitraryHash160
|
||||
-- | Arbitrary pay-to-witness script hash
|
||||
arbitraryWitnessScriptAddress :: Gen Address
|
||||
arbitraryWitnessScriptAddress = WitnessPubKeyAddress <$> arbitraryHash160
|
||||
|
||||
arbitraryWitnessAddress :: Gen Address
|
||||
arbitraryWitnessAddress = do
|
||||
ver <- choose (0, 16)
|
||||
len <- choose (2, 40)
|
||||
ws <- vectorOf len arbitrary
|
||||
let bs = B.pack ws
|
||||
return $ WitnessAddress ver bs
|
||||
|
@ -10,6 +10,7 @@ Portability : POSIX
|
||||
module Haskoin.Util.Arbitrary.Script where
|
||||
|
||||
import Crypto.Secp256k1
|
||||
import qualified Data.ByteString as B
|
||||
import Data.Maybe
|
||||
import Data.Word
|
||||
import Haskoin.Address
|
||||
@ -239,7 +240,10 @@ arbitraryScriptOutput net =
|
||||
, arbitraryDCOutput
|
||||
] ++
|
||||
if getSegWit net
|
||||
then [arbitraryWPKHashOutput, arbitraryWSHOutput]
|
||||
then [ arbitraryWPKHashOutput
|
||||
, arbitraryWSHOutput
|
||||
, arbitraryWitOutput
|
||||
]
|
||||
else []
|
||||
|
||||
-- | Arbitrary 'ScriptOutput' of type 'PayPK', 'PayPKHash' or 'PayMS'
|
||||
@ -268,6 +272,14 @@ arbitraryWPKHashOutput = PayWitnessPKHash <$> arbitraryHash160
|
||||
arbitraryWSHOutput :: Gen ScriptOutput
|
||||
arbitraryWSHOutput = PayWitnessScriptHash <$> arbitraryHash256
|
||||
|
||||
arbitraryWitOutput :: Gen ScriptOutput
|
||||
arbitraryWitOutput = do
|
||||
ver <- choose (0, 16)
|
||||
len <- choose (2, 40)
|
||||
ws <- vectorOf len arbitrary
|
||||
let bs = B.pack ws
|
||||
return $ PayWitness ver bs
|
||||
|
||||
-- | Arbitrary 'ScriptOutput' of type 'PayMS'.
|
||||
arbitraryMSOutput :: Gen ScriptOutput
|
||||
arbitraryMSOutput = do
|
||||
|
Loading…
Reference in New Issue
Block a user