haskoin 0.0.2.1 → 0.1.0
raw patch · 86 files changed
+5598/−3417 lines, 86 filesdep +asyncdep +deepseqdep +json-rpcdep −conduitdep −conduit-extradep −scientificdep ~QuickCheckdep ~aesondep ~basePVP ok
version bump matches the API change (PVP)
Dependencies added: async, deepseq, json-rpc
Dependencies removed: conduit, conduit-extra, scientific, text-icu, vector
Dependency ranges changed: QuickCheck, aeson, base, bytestring, either, mtl, text
API changes (from Hackage documentation)
- Network.Haskoin.Crypto: addPrvKeys :: PrvKey -> Hash256 -> Maybe PrvKey
- Network.Haskoin.Crypto: addPubKeys :: PubKey -> Hash256 -> Maybe PubKey
- Network.Haskoin.Crypto: anyToSeed :: Passphrase -> Mnemonic -> Seed
- Network.Haskoin.Crypto: bloomIsValid :: BloomFilter -> Bool
- Network.Haskoin.Crypto: bloomUpdateEmptyFull :: BloomFilter -> BloomFilter
- Network.Haskoin.Crypto: data CheckSum32
- Network.Haskoin.Crypto: english :: [Text]
- Network.Haskoin.Crypto: fromMnemonic :: WordList -> Mnemonic -> Either String Entropy
- Network.Haskoin.Crypto: type Hash160 = Word160
- Network.Haskoin.Crypto: type Hash256 = Word256
- Network.Haskoin.Crypto: type Hash512 = Word512
- Network.Haskoin.Crypto: type WordList = [Text]
- Network.Haskoin.Protocol: OPCODE :: PushDataType
- Network.Haskoin.Protocol: OPDATA1 :: PushDataType
- Network.Haskoin.Protocol: OPDATA2 :: PushDataType
- Network.Haskoin.Protocol: OPDATA4 :: PushDataType
- Network.Haskoin.Protocol: OP_0 :: ScriptOp
- Network.Haskoin.Protocol: OP_1 :: ScriptOp
- Network.Haskoin.Protocol: OP_10 :: ScriptOp
- Network.Haskoin.Protocol: OP_11 :: ScriptOp
- Network.Haskoin.Protocol: OP_12 :: ScriptOp
- Network.Haskoin.Protocol: OP_13 :: ScriptOp
- Network.Haskoin.Protocol: OP_14 :: ScriptOp
- Network.Haskoin.Protocol: OP_15 :: ScriptOp
- Network.Haskoin.Protocol: OP_16 :: ScriptOp
- Network.Haskoin.Protocol: OP_1NEGATE :: ScriptOp
- Network.Haskoin.Protocol: OP_2 :: ScriptOp
- Network.Haskoin.Protocol: OP_3 :: ScriptOp
- Network.Haskoin.Protocol: OP_4 :: ScriptOp
- Network.Haskoin.Protocol: OP_5 :: ScriptOp
- Network.Haskoin.Protocol: OP_6 :: ScriptOp
- Network.Haskoin.Protocol: OP_7 :: ScriptOp
- Network.Haskoin.Protocol: OP_8 :: ScriptOp
- Network.Haskoin.Protocol: OP_9 :: ScriptOp
- Network.Haskoin.Protocol: OP_CHECKMULTISIG :: ScriptOp
- Network.Haskoin.Protocol: OP_CHECKSIG :: ScriptOp
- Network.Haskoin.Protocol: OP_DUP :: ScriptOp
- Network.Haskoin.Protocol: OP_EQUAL :: ScriptOp
- Network.Haskoin.Protocol: OP_EQUALVERIFY :: ScriptOp
- Network.Haskoin.Protocol: OP_HASH160 :: ScriptOp
- Network.Haskoin.Protocol: OP_INVALIDOPCODE :: Word8 -> ScriptOp
- Network.Haskoin.Protocol: OP_PUBKEY :: PubKey -> ScriptOp
- Network.Haskoin.Protocol: OP_PUSHDATA :: ByteString -> PushDataType -> ScriptOp
- Network.Haskoin.Protocol: OP_VERIFY :: ScriptOp
- Network.Haskoin.Protocol: Script :: [ScriptOp] -> Script
- Network.Haskoin.Protocol: blockid :: BlockHeader -> Hash256
- Network.Haskoin.Protocol: bloomEmpty :: BloomFilter -> Bool
- Network.Haskoin.Protocol: bloomFull :: BloomFilter -> Bool
- Network.Haskoin.Protocol: cbid :: CoinbaseTx -> Hash256
- Network.Haskoin.Protocol: data PushDataType
- Network.Haskoin.Protocol: data Script
- Network.Haskoin.Protocol: data ScriptOp
- Network.Haskoin.Protocol: decodeScriptOps :: ByteString -> Either String Script
- Network.Haskoin.Protocol: decodeTxid :: String -> Maybe Hash256
- Network.Haskoin.Protocol: encodeScriptOps :: Script -> ByteString
- Network.Haskoin.Protocol: encodeTxid :: Hash256 -> String
- Network.Haskoin.Protocol: getScriptOps :: Get [ScriptOp]
- Network.Haskoin.Protocol: opPushData :: ByteString -> ScriptOp
- Network.Haskoin.Protocol: putScriptOps :: [ScriptOp] -> Put
- Network.Haskoin.Protocol: scriptOps :: Script -> [ScriptOp]
- Network.Haskoin.Protocol: txid :: Tx -> Hash256
- Network.Haskoin.Script: data ScriptHashInput
- Network.Haskoin.Script: decodeScriptHash :: Script -> Either String ScriptHashInput
- Network.Haskoin.Script: encodeScriptHash :: ScriptHashInput -> Script
- Network.Haskoin.Script: getInputMulSigRequired :: ScriptInput -> !Int
- Network.Haskoin.Script: spendSHInput :: ScriptHashInput -> ScriptInput
- Network.Haskoin.Script: spendSHOutput :: ScriptHashInput -> RedeemScript
- Network.Haskoin.Stratum: AddrStatus :: Hash256 -> StratumResponse
- Network.Haskoin.Stratum: AddressBalance :: Balance -> StratumResponse
- Network.Haskoin.Stratum: AddressHistory :: [TxHeight] -> StratumResponse
- Network.Haskoin.Stratum: AddressUnspent :: [Coin] -> StratumResponse
- Network.Haskoin.Stratum: Balance :: Word64 -> Word64 -> Balance
- Network.Haskoin.Stratum: BroadcastId :: Hash256 -> StratumResponse
- Network.Haskoin.Stratum: Coin :: OutPoint -> TxHeight -> Word64 -> Coin
- Network.Haskoin.Stratum: ErrObj :: Int -> String -> Maybe e -> Error e v
- Network.Haskoin.Stratum: ErrVal :: v -> Error e v
- Network.Haskoin.Stratum: IntId :: Int -> Id
- Network.Haskoin.Stratum: MsgRequest :: (Request j) -> Message j r e v
- Network.Haskoin.Stratum: MsgResponse :: (Response r e v) -> Message j r e v
- Network.Haskoin.Stratum: NotifAddress :: Address -> Hash256 -> StratumNotif
- Network.Haskoin.Stratum: QueryBalance :: Address -> StratumQuery
- Network.Haskoin.Stratum: QueryBroadcast :: Tx -> StratumQuery
- Network.Haskoin.Stratum: QueryHistory :: Address -> StratumQuery
- Network.Haskoin.Stratum: QueryTx :: Hash256 -> StratumQuery
- Network.Haskoin.Stratum: QueryUnspent :: Address -> StratumQuery
- Network.Haskoin.Stratum: QueryVersion :: Text -> Text -> StratumQuery
- Network.Haskoin.Stratum: Request :: Method -> Maybe j -> Maybe Id -> Request j
- Network.Haskoin.Stratum: Response :: Result r e v -> Maybe Id -> Response r e v
- Network.Haskoin.Stratum: ServerVersion :: String -> StratumResponse
- Network.Haskoin.Stratum: SubAddress :: Address -> StratumQuery
- Network.Haskoin.Stratum: Transaction :: Tx -> StratumResponse
- Network.Haskoin.Stratum: TxHeight :: Word -> Hash256 -> TxHeight
- Network.Haskoin.Stratum: TxtId :: Text -> Id
- Network.Haskoin.Stratum: balConfirmed :: Balance -> Word64
- Network.Haskoin.Stratum: balUnconfirmed :: Balance -> Word64
- Network.Haskoin.Stratum: coinOutPoint :: Coin -> OutPoint
- Network.Haskoin.Stratum: coinTxHeight :: Coin -> TxHeight
- Network.Haskoin.Stratum: coinValue :: Coin -> Word64
- Network.Haskoin.Stratum: data Balance
- Network.Haskoin.Stratum: data Coin
- Network.Haskoin.Stratum: data Error e v
- Network.Haskoin.Stratum: data Id
- Network.Haskoin.Stratum: data Message j r e v
- Network.Haskoin.Stratum: data Request j
- Network.Haskoin.Stratum: data Response r e v
- Network.Haskoin.Stratum: data Session q r e v j
- Network.Haskoin.Stratum: data StratumQuery
- Network.Haskoin.Stratum: data StratumResponse
- Network.Haskoin.Stratum: data TxHeight
- Network.Haskoin.Stratum: errCode :: Error e v -> Int
- Network.Haskoin.Stratum: errData :: Error e v -> Maybe e
- Network.Haskoin.Stratum: errInternal :: ToJSON e => Maybe e -> Error e v
- Network.Haskoin.Stratum: errMeth :: ToJSON e => Maybe e -> Error e v
- Network.Haskoin.Stratum: errMsg :: Error e v -> String
- Network.Haskoin.Stratum: errParams :: ToJSON e => Maybe e -> Error e v
- Network.Haskoin.Stratum: errParse :: ToJSON e => Maybe e -> Error e v
- Network.Haskoin.Stratum: errReq :: ToJSON e => Maybe e -> Error e v
- Network.Haskoin.Stratum: errStr :: Error e Value -> String
- Network.Haskoin.Stratum: errVal :: Error e v -> v
- Network.Haskoin.Stratum: initSession :: MonadIO m => Maybe (RequestParser j) -> m (Session q r e v j)
- Network.Haskoin.Stratum: instance Eq Balance
- Network.Haskoin.Stratum: instance Eq Coin
- Network.Haskoin.Stratum: instance Eq StratumQuery
- Network.Haskoin.Stratum: instance Eq StratumResponse
- Network.Haskoin.Stratum: instance Eq TxHeight
- Network.Haskoin.Stratum: instance FromJSON Balance
- Network.Haskoin.Stratum: instance FromJSON Coin
- Network.Haskoin.Stratum: instance FromJSON TxHeight
- Network.Haskoin.Stratum: instance Show Balance
- Network.Haskoin.Stratum: instance Show Coin
- Network.Haskoin.Stratum: instance Show StratumQuery
- Network.Haskoin.Stratum: instance Show StratumResponse
- Network.Haskoin.Stratum: instance Show TxHeight
- Network.Haskoin.Stratum: instance ToJSON Coin
- Network.Haskoin.Stratum: instance ToJSON StratumQuery
- Network.Haskoin.Stratum: instance ToJSON TxHeight
- Network.Haskoin.Stratum: intId :: Id -> Int
- Network.Haskoin.Stratum: leftStr :: Either (Error e Value) r -> Either String r
- Network.Haskoin.Stratum: newNotif :: MonadIO m => Session q r e v j -> q -> m ()
- Network.Haskoin.Stratum: newReq :: MonadIO m => Session q r e v j -> (Int -> q) -> ResponseParser r e v -> m Int
- Network.Haskoin.Stratum: newStratumReq :: MonadIO m => StratumSession -> StratumQuery -> m Int
- Network.Haskoin.Stratum: notifAddr :: StratumNotif -> Address
- Network.Haskoin.Stratum: notifAddrStatus :: StratumNotif -> Hash256
- Network.Haskoin.Stratum: numericId :: Id -> Either String Int
- Network.Haskoin.Stratum: parseNotif :: RequestValue -> Parser NotifStratum
- Network.Haskoin.Stratum: parseResult :: StratumQuery -> ResultValue -> Parser ResultStratum
- Network.Haskoin.Stratum: queryAddr :: StratumQuery -> Address
- Network.Haskoin.Stratum: queryClientVer :: StratumQuery -> Text
- Network.Haskoin.Stratum: queryProtoVer :: StratumQuery -> Text
- Network.Haskoin.Stratum: queryTx :: StratumQuery -> Tx
- Network.Haskoin.Stratum: queryTxid :: StratumQuery -> Hash256
- Network.Haskoin.Stratum: reqId :: Request j -> Maybe Id
- Network.Haskoin.Stratum: reqMethod :: Request j -> Method
- Network.Haskoin.Stratum: reqParams :: Request j -> Maybe j
- Network.Haskoin.Stratum: reqSource :: (MonadIO m, ToJSON q) => Session q r e v j -> Source m ByteString
- Network.Haskoin.Stratum: resConduit :: MonadIO m => Session q r e v j -> Conduit ByteString m (Either String (Message j r e v))
- Network.Haskoin.Stratum: resId :: Response r e v -> Maybe Id
- Network.Haskoin.Stratum: resResult :: Response r e v -> Result r e v
- Network.Haskoin.Stratum: stratumBalance :: StratumResponse -> Balance
- Network.Haskoin.Stratum: stratumServerVer :: StratumResponse -> String
- Network.Haskoin.Stratum: stratumTxid :: StratumResponse -> Hash256
- Network.Haskoin.Stratum: toRequest :: StratumQuery -> Int -> RequestStratum
- Network.Haskoin.Stratum: txHeightBlock :: TxHeight -> Word
- Network.Haskoin.Stratum: txHeightId :: TxHeight -> Hash256
- Network.Haskoin.Stratum: txtId :: Id -> Text
- Network.Haskoin.Stratum: type ErrorValue = Error Value String
- Network.Haskoin.Stratum: type MessageStratum = Message StratumNotif StratumResponse Value String
- Network.Haskoin.Stratum: type MessageValue = Message Value Value Value String
- Network.Haskoin.Stratum: type Method = Text
- Network.Haskoin.Stratum: type NotifStratum = Request StratumNotif
- Network.Haskoin.Stratum: type RequestStratum = Request StratumQuery
- Network.Haskoin.Stratum: type RequestValue = Request Value
- Network.Haskoin.Stratum: type ResponseStratum = Response StratumResponse Value String
- Network.Haskoin.Stratum: type ResponseValue = Response Value Value String
- Network.Haskoin.Stratum: type Result r e v = Either (Error e v) r
- Network.Haskoin.Stratum: type ResultStratum = Result StratumResponse Value String
- Network.Haskoin.Stratum: type ResultValue = Result Value Value String
- Network.Haskoin.Stratum: type StratumSession = Session RequestStratum StratumResponse Value String StratumNotif
- Network.Haskoin.Transaction: SigInputSH :: Script -> OutPoint -> Script -> SigHash -> SigInput
- Network.Haskoin.Transaction: coinTxOut :: Coin -> TxOut
- Network.Haskoin.Transaction: isTxComplete :: Tx -> Bool
- Network.Haskoin.Transaction: sigRedeem :: SigInput -> Script
- Network.Haskoin.Util: Broken :: String -> Build a
- Network.Haskoin.Util: BuildT :: m (Build a) -> BuildT m a
- Network.Haskoin.Util: Complete :: a -> Build a
- Network.Haskoin.Util: Partial :: a -> Build a
- Network.Haskoin.Util: addrPrefix :: Word8
- Network.Haskoin.Util: buildToEither :: Build a -> Either String a
- Network.Haskoin.Util: data Build a
- Network.Haskoin.Util: eitherToBuild :: Either String a -> Build a
- Network.Haskoin.Util: extPubKeyPrefix :: Word32
- Network.Haskoin.Util: extSecretPrefix :: Word32
- Network.Haskoin.Util: genesisHeader :: [Integer]
- Network.Haskoin.Util: guardPartial :: Bool -> Build ()
- Network.Haskoin.Util: haskoinUserAgent :: String
- Network.Haskoin.Util: isBroken :: Build a -> Bool
- Network.Haskoin.Util: isComplete :: Build a -> Bool
- Network.Haskoin.Util: isPartial :: Build a -> Bool
- Network.Haskoin.Util: liftBuild :: Monad m => Build a -> BuildT m a
- Network.Haskoin.Util: maxBlockSize :: Int
- Network.Haskoin.Util: networkMagic :: Word32
- Network.Haskoin.Util: newtype BuildT m a
- Network.Haskoin.Util: runBroken :: Build a -> String
- Network.Haskoin.Util: runBuild :: Build a -> a
- Network.Haskoin.Util: runBuildT :: BuildT m a -> m (Build a)
- Network.Haskoin.Util: scriptPrefix :: Word8
- Network.Haskoin.Util: secretPrefix :: Word8
- Network.Haskoin.Util: walletFile :: String
+ Network.Haskoin.Constants: addrPrefix :: Word8
+ Network.Haskoin.Constants: allowMinDifficultyBlocks :: Bool
+ Network.Haskoin.Constants: checkpoints :: [(Int, BlockHash)]
+ Network.Haskoin.Constants: defaultPort :: Int
+ Network.Haskoin.Constants: extPubKeyPrefix :: Word32
+ Network.Haskoin.Constants: extSecretPrefix :: Word32
+ Network.Haskoin.Constants: genesisHeader :: BlockHeader
+ Network.Haskoin.Constants: haskoinUserAgent :: String
+ Network.Haskoin.Constants: maxBlockSize :: Int
+ Network.Haskoin.Constants: networkMagic :: Word32
+ Network.Haskoin.Constants: networkName :: String
+ Network.Haskoin.Constants: scriptPrefix :: Word8
+ Network.Haskoin.Constants: secretPrefix :: Word8
+ Network.Haskoin.Crypto: cbHash :: CoinbaseTx -> TxHash
+ Network.Haskoin.Crypto: decodeBlockHashLE :: String -> Maybe BlockHash
+ Network.Haskoin.Crypto: decodeTxHashLE :: String -> Maybe TxHash
+ Network.Haskoin.Crypto: encodeBlockHashLE :: BlockHash -> String
+ Network.Haskoin.Crypto: encodeTxHashLE :: TxHash -> String
+ Network.Haskoin.Crypto: hashSha1 :: ByteString -> Word160
+ Network.Haskoin.Crypto: hashSha1BS :: ByteString -> ByteString
+ Network.Haskoin.Crypto: headerHash :: BlockHeader -> BlockHash
+ Network.Haskoin.Crypto: isBloomEmpty :: BloomFilter -> Bool
+ Network.Haskoin.Crypto: isBloomFull :: BloomFilter -> Bool
+ Network.Haskoin.Crypto: isBloomValid :: BloomFilter -> Bool
+ Network.Haskoin.Crypto: txHash :: Tx -> TxHash
+ Network.Haskoin.Crypto: type BlockHash = BigWord Mod256Block
+ Network.Haskoin.Crypto: type CheckSum32 = Word32
+ Network.Haskoin.Crypto: type TxHash = BigWord Mod256Tx
+ Network.Haskoin.Internals: AccPrvKey :: XPrvKey -> AccPrvKey
+ Network.Haskoin.Internals: AccPubKey :: XPubKey -> AccPubKey
+ Network.Haskoin.Internals: Addr :: ![NetworkAddressTime] -> Addr
+ Network.Haskoin.Internals: AddrPrvKey :: XPrvKey -> AddrPrvKey
+ Network.Haskoin.Internals: AddrPubKey :: XPubKey -> AddrPubKey
+ Network.Haskoin.Internals: Alert :: !VarString -> !VarString -> Alert
+ Network.Haskoin.Internals: BigWord :: Integer -> BigWord n
+ Network.Haskoin.Internals: Block :: !BlockHeader -> !CoinbaseTx -> ![Tx] -> Block
+ Network.Haskoin.Internals: BlockHeader :: !Word32 -> !BlockHash -> !Word256 -> !Word32 -> !Word32 -> !Word32 -> BlockHeader
+ Network.Haskoin.Internals: BloomFilter :: !(Seq Word8) -> !Word32 -> !Word32 -> !BloomFlags -> BloomFilter
+ Network.Haskoin.Internals: BloomUpdateAll :: BloomFlags
+ Network.Haskoin.Internals: BloomUpdateNone :: BloomFlags
+ Network.Haskoin.Internals: BloomUpdateP2PubKeyOnly :: BloomFlags
+ Network.Haskoin.Internals: Coin :: !Word64 -> !ScriptOutput -> !OutPoint -> !(Maybe RedeemScript) -> Coin
+ Network.Haskoin.Internals: CoinbaseTx :: !Word32 -> !OutPoint -> !ByteString -> !Word32 -> ![TxOut] -> !Word32 -> CoinbaseTx
+ Network.Haskoin.Internals: FilterAdd :: ByteString -> FilterAdd
+ Network.Haskoin.Internals: FilterLoad :: BloomFilter -> FilterLoad
+ Network.Haskoin.Internals: GetBlocks :: !Word32 -> !BlockLocator -> !BlockHash -> GetBlocks
+ Network.Haskoin.Internals: GetData :: ![InvVector] -> GetData
+ Network.Haskoin.Internals: GetHeaders :: !Word32 -> !BlockLocator -> !BlockHash -> GetHeaders
+ Network.Haskoin.Internals: Headers :: ![BlockHeaderCount] -> Headers
+ Network.Haskoin.Internals: InfPoint :: Point
+ Network.Haskoin.Internals: Inv :: ![InvVector] -> Inv
+ Network.Haskoin.Internals: InvBlock :: InvType
+ Network.Haskoin.Internals: InvError :: InvType
+ Network.Haskoin.Internals: InvMerkleBlock :: InvType
+ Network.Haskoin.Internals: InvTx :: InvType
+ Network.Haskoin.Internals: InvVector :: !InvType -> !Word256 -> InvVector
+ Network.Haskoin.Internals: MAddr :: !Addr -> Message
+ Network.Haskoin.Internals: MAlert :: !Alert -> Message
+ Network.Haskoin.Internals: MBlock :: !Block -> Message
+ Network.Haskoin.Internals: MCAddr :: MessageCommand
+ Network.Haskoin.Internals: MCAlert :: MessageCommand
+ Network.Haskoin.Internals: MCBlock :: MessageCommand
+ Network.Haskoin.Internals: MCFilterAdd :: MessageCommand
+ Network.Haskoin.Internals: MCFilterClear :: MessageCommand
+ Network.Haskoin.Internals: MCFilterLoad :: MessageCommand
+ Network.Haskoin.Internals: MCGetAddr :: MessageCommand
+ Network.Haskoin.Internals: MCGetBlocks :: MessageCommand
+ Network.Haskoin.Internals: MCGetData :: MessageCommand
+ Network.Haskoin.Internals: MCGetHeaders :: MessageCommand
+ Network.Haskoin.Internals: MCHeaders :: MessageCommand
+ Network.Haskoin.Internals: MCInv :: MessageCommand
+ Network.Haskoin.Internals: MCMerkleBlock :: MessageCommand
+ Network.Haskoin.Internals: MCNotFound :: MessageCommand
+ Network.Haskoin.Internals: MCPing :: MessageCommand
+ Network.Haskoin.Internals: MCPong :: MessageCommand
+ Network.Haskoin.Internals: MCReject :: MessageCommand
+ Network.Haskoin.Internals: MCTx :: MessageCommand
+ Network.Haskoin.Internals: MCVerAck :: MessageCommand
+ Network.Haskoin.Internals: MCVersion :: MessageCommand
+ Network.Haskoin.Internals: MFilterAdd :: !FilterAdd -> Message
+ Network.Haskoin.Internals: MFilterClear :: Message
+ Network.Haskoin.Internals: MFilterLoad :: !FilterLoad -> Message
+ Network.Haskoin.Internals: MGetAddr :: Message
+ Network.Haskoin.Internals: MGetBlocks :: !GetBlocks -> Message
+ Network.Haskoin.Internals: MGetData :: !GetData -> Message
+ Network.Haskoin.Internals: MGetHeaders :: !GetHeaders -> Message
+ Network.Haskoin.Internals: MHeaders :: !Headers -> Message
+ Network.Haskoin.Internals: MInv :: !Inv -> Message
+ Network.Haskoin.Internals: MMerkleBlock :: !MerkleBlock -> Message
+ Network.Haskoin.Internals: MNotFound :: !NotFound -> Message
+ Network.Haskoin.Internals: MPing :: !Ping -> Message
+ Network.Haskoin.Internals: MPong :: !Pong -> Message
+ Network.Haskoin.Internals: MReject :: Reject -> Message
+ Network.Haskoin.Internals: MTx :: !Tx -> Message
+ Network.Haskoin.Internals: MVerAck :: Message
+ Network.Haskoin.Internals: MVersion :: !Version -> Message
+ Network.Haskoin.Internals: MasterKey :: XPrvKey -> MasterKey
+ Network.Haskoin.Internals: MerkleBlock :: !BlockHeader -> !Word32 -> ![Word256] -> ![Bool] -> MerkleBlock
+ Network.Haskoin.Internals: MessageHeader :: !Word32 -> !MessageCommand -> !Word32 -> !CheckSum32 -> MessageHeader
+ Network.Haskoin.Internals: NetworkAddress :: !Word64 -> !(Word64, Word64) -> !Word16 -> NetworkAddress
+ Network.Haskoin.Internals: NotFound :: ![InvVector] -> NotFound
+ Network.Haskoin.Internals: OPCODE :: PushDataType
+ Network.Haskoin.Internals: OPDATA1 :: PushDataType
+ Network.Haskoin.Internals: OPDATA2 :: PushDataType
+ Network.Haskoin.Internals: OPDATA4 :: PushDataType
+ Network.Haskoin.Internals: OP_0 :: ScriptOp
+ Network.Haskoin.Internals: OP_0NOTEQUAL :: ScriptOp
+ Network.Haskoin.Internals: OP_1 :: ScriptOp
+ Network.Haskoin.Internals: OP_10 :: ScriptOp
+ Network.Haskoin.Internals: OP_11 :: ScriptOp
+ Network.Haskoin.Internals: OP_12 :: ScriptOp
+ Network.Haskoin.Internals: OP_13 :: ScriptOp
+ Network.Haskoin.Internals: OP_14 :: ScriptOp
+ Network.Haskoin.Internals: OP_15 :: ScriptOp
+ Network.Haskoin.Internals: OP_16 :: ScriptOp
+ Network.Haskoin.Internals: OP_1ADD :: ScriptOp
+ Network.Haskoin.Internals: OP_1NEGATE :: ScriptOp
+ Network.Haskoin.Internals: OP_1SUB :: ScriptOp
+ Network.Haskoin.Internals: OP_2 :: ScriptOp
+ Network.Haskoin.Internals: OP_2DIV :: ScriptOp
+ Network.Haskoin.Internals: OP_2DROP :: ScriptOp
+ Network.Haskoin.Internals: OP_2DUP :: ScriptOp
+ Network.Haskoin.Internals: OP_2MUL :: ScriptOp
+ Network.Haskoin.Internals: OP_2OVER :: ScriptOp
+ Network.Haskoin.Internals: OP_2ROT :: ScriptOp
+ Network.Haskoin.Internals: OP_2SWAP :: ScriptOp
+ Network.Haskoin.Internals: OP_3 :: ScriptOp
+ Network.Haskoin.Internals: OP_3DUP :: ScriptOp
+ Network.Haskoin.Internals: OP_4 :: ScriptOp
+ Network.Haskoin.Internals: OP_5 :: ScriptOp
+ Network.Haskoin.Internals: OP_6 :: ScriptOp
+ Network.Haskoin.Internals: OP_7 :: ScriptOp
+ Network.Haskoin.Internals: OP_8 :: ScriptOp
+ Network.Haskoin.Internals: OP_9 :: ScriptOp
+ Network.Haskoin.Internals: OP_ABS :: ScriptOp
+ Network.Haskoin.Internals: OP_ADD :: ScriptOp
+ Network.Haskoin.Internals: OP_AND :: ScriptOp
+ Network.Haskoin.Internals: OP_BOOLAND :: ScriptOp
+ Network.Haskoin.Internals: OP_BOOLOR :: ScriptOp
+ Network.Haskoin.Internals: OP_CAT :: ScriptOp
+ Network.Haskoin.Internals: OP_CHECKMULTISIG :: ScriptOp
+ Network.Haskoin.Internals: OP_CHECKMULTISIGVERIFY :: ScriptOp
+ Network.Haskoin.Internals: OP_CHECKSIG :: ScriptOp
+ Network.Haskoin.Internals: OP_CHECKSIGVERIFY :: ScriptOp
+ Network.Haskoin.Internals: OP_CODESEPARATOR :: ScriptOp
+ Network.Haskoin.Internals: OP_DEPTH :: ScriptOp
+ Network.Haskoin.Internals: OP_DIV :: ScriptOp
+ Network.Haskoin.Internals: OP_DROP :: ScriptOp
+ Network.Haskoin.Internals: OP_DUP :: ScriptOp
+ Network.Haskoin.Internals: OP_ELSE :: ScriptOp
+ Network.Haskoin.Internals: OP_ENDIF :: ScriptOp
+ Network.Haskoin.Internals: OP_EQUAL :: ScriptOp
+ Network.Haskoin.Internals: OP_EQUALVERIFY :: ScriptOp
+ Network.Haskoin.Internals: OP_FROMALTSTACK :: ScriptOp
+ Network.Haskoin.Internals: OP_GREATERTHAN :: ScriptOp
+ Network.Haskoin.Internals: OP_GREATERTHANOREQUAL :: ScriptOp
+ Network.Haskoin.Internals: OP_HASH160 :: ScriptOp
+ Network.Haskoin.Internals: OP_HASH256 :: ScriptOp
+ Network.Haskoin.Internals: OP_IF :: ScriptOp
+ Network.Haskoin.Internals: OP_IFDUP :: ScriptOp
+ Network.Haskoin.Internals: OP_INVALIDOPCODE :: !Word8 -> ScriptOp
+ Network.Haskoin.Internals: OP_INVERT :: ScriptOp
+ Network.Haskoin.Internals: OP_LEFT :: ScriptOp
+ Network.Haskoin.Internals: OP_LESSTHAN :: ScriptOp
+ Network.Haskoin.Internals: OP_LESSTHANOREQUAL :: ScriptOp
+ Network.Haskoin.Internals: OP_LSHIFT :: ScriptOp
+ Network.Haskoin.Internals: OP_MAX :: ScriptOp
+ Network.Haskoin.Internals: OP_MIN :: ScriptOp
+ Network.Haskoin.Internals: OP_MOD :: ScriptOp
+ Network.Haskoin.Internals: OP_MUL :: ScriptOp
+ Network.Haskoin.Internals: OP_NEGATE :: ScriptOp
+ Network.Haskoin.Internals: OP_NIP :: ScriptOp
+ Network.Haskoin.Internals: OP_NOP :: ScriptOp
+ Network.Haskoin.Internals: OP_NOP1 :: ScriptOp
+ Network.Haskoin.Internals: OP_NOP10 :: ScriptOp
+ Network.Haskoin.Internals: OP_NOP2 :: ScriptOp
+ Network.Haskoin.Internals: OP_NOP3 :: ScriptOp
+ Network.Haskoin.Internals: OP_NOP4 :: ScriptOp
+ Network.Haskoin.Internals: OP_NOP5 :: ScriptOp
+ Network.Haskoin.Internals: OP_NOP6 :: ScriptOp
+ Network.Haskoin.Internals: OP_NOP7 :: ScriptOp
+ Network.Haskoin.Internals: OP_NOP8 :: ScriptOp
+ Network.Haskoin.Internals: OP_NOP9 :: ScriptOp
+ Network.Haskoin.Internals: OP_NOT :: ScriptOp
+ Network.Haskoin.Internals: OP_NOTIF :: ScriptOp
+ Network.Haskoin.Internals: OP_NUMEQUAL :: ScriptOp
+ Network.Haskoin.Internals: OP_NUMEQUALVERIFY :: ScriptOp
+ Network.Haskoin.Internals: OP_NUMNOTEQUAL :: ScriptOp
+ Network.Haskoin.Internals: OP_OR :: ScriptOp
+ Network.Haskoin.Internals: OP_OVER :: ScriptOp
+ Network.Haskoin.Internals: OP_PICK :: ScriptOp
+ Network.Haskoin.Internals: OP_PUBKEY :: ScriptOp
+ Network.Haskoin.Internals: OP_PUBKEYHASH :: ScriptOp
+ Network.Haskoin.Internals: OP_PUSHDATA :: !ByteString -> !PushDataType -> ScriptOp
+ Network.Haskoin.Internals: OP_RESERVED :: ScriptOp
+ Network.Haskoin.Internals: OP_RESERVED1 :: ScriptOp
+ Network.Haskoin.Internals: OP_RESERVED2 :: ScriptOp
+ Network.Haskoin.Internals: OP_RETURN :: ScriptOp
+ Network.Haskoin.Internals: OP_RIGHT :: ScriptOp
+ Network.Haskoin.Internals: OP_RIPEMD160 :: ScriptOp
+ Network.Haskoin.Internals: OP_ROLL :: ScriptOp
+ Network.Haskoin.Internals: OP_ROT :: ScriptOp
+ Network.Haskoin.Internals: OP_RSHIFT :: ScriptOp
+ Network.Haskoin.Internals: OP_SHA1 :: ScriptOp
+ Network.Haskoin.Internals: OP_SHA256 :: ScriptOp
+ Network.Haskoin.Internals: OP_SIZE :: ScriptOp
+ Network.Haskoin.Internals: OP_SUB :: ScriptOp
+ Network.Haskoin.Internals: OP_SUBSTR :: ScriptOp
+ Network.Haskoin.Internals: OP_SWAP :: ScriptOp
+ Network.Haskoin.Internals: OP_TOALTSTACK :: ScriptOp
+ Network.Haskoin.Internals: OP_TUCK :: ScriptOp
+ Network.Haskoin.Internals: OP_VER :: ScriptOp
+ Network.Haskoin.Internals: OP_VERIF :: ScriptOp
+ Network.Haskoin.Internals: OP_VERIFY :: ScriptOp
+ Network.Haskoin.Internals: OP_VERNOTIF :: ScriptOp
+ Network.Haskoin.Internals: OP_WITHIN :: ScriptOp
+ Network.Haskoin.Internals: OP_XOR :: ScriptOp
+ Network.Haskoin.Internals: OutPoint :: !TxHash -> !Word32 -> OutPoint
+ Network.Haskoin.Internals: PayMulSig :: ![PubKey] -> !Int -> ScriptOutput
+ Network.Haskoin.Internals: PayPK :: !PubKey -> ScriptOutput
+ Network.Haskoin.Internals: PayPKHash :: !Address -> ScriptOutput
+ Network.Haskoin.Internals: PayScriptHash :: !Address -> ScriptOutput
+ Network.Haskoin.Internals: Ping :: Word64 -> Ping
+ Network.Haskoin.Internals: Pong :: Word64 -> Pong
+ Network.Haskoin.Internals: PrvKey :: !FieldN -> PrvKey
+ Network.Haskoin.Internals: PrvKeyU :: !FieldN -> PrvKey
+ Network.Haskoin.Internals: PubKey :: !Point -> PubKey
+ Network.Haskoin.Internals: PubKeyAddress :: Word160 -> Address
+ Network.Haskoin.Internals: PubKeyU :: !Point -> PubKey
+ Network.Haskoin.Internals: RegularInput :: SimpleInput -> ScriptInput
+ Network.Haskoin.Internals: Reject :: !MessageCommand -> !RejectCode -> !VarString -> Reject
+ Network.Haskoin.Internals: RejectCheckpoint :: RejectCode
+ Network.Haskoin.Internals: RejectDuplicate :: RejectCode
+ Network.Haskoin.Internals: RejectDust :: RejectCode
+ Network.Haskoin.Internals: RejectInsufficientFee :: RejectCode
+ Network.Haskoin.Internals: RejectInvalid :: RejectCode
+ Network.Haskoin.Internals: RejectMalformed :: RejectCode
+ Network.Haskoin.Internals: RejectNonStandard :: RejectCode
+ Network.Haskoin.Internals: RejectObsolete :: RejectCode
+ Network.Haskoin.Internals: Script :: ![ScriptOp] -> Script
+ Network.Haskoin.Internals: ScriptAddress :: Word160 -> Address
+ Network.Haskoin.Internals: ScriptHashInput :: SimpleInput -> RedeemScript -> ScriptInput
+ Network.Haskoin.Internals: SigAll :: !Bool -> SigHash
+ Network.Haskoin.Internals: SigInput :: !ScriptOutput -> !OutPoint -> !SigHash -> !(Maybe RedeemScript) -> SigInput
+ Network.Haskoin.Internals: SigNone :: !Bool -> SigHash
+ Network.Haskoin.Internals: SigSingle :: !Bool -> SigHash
+ Network.Haskoin.Internals: SigUnknown :: !Bool -> !Word8 -> SigHash
+ Network.Haskoin.Internals: Signature :: !FieldN -> !FieldN -> Signature
+ Network.Haskoin.Internals: SpendMulSig :: ![TxSignature] -> SimpleInput
+ Network.Haskoin.Internals: SpendPK :: !TxSignature -> SimpleInput
+ Network.Haskoin.Internals: SpendPKHash :: !TxSignature -> !PubKey -> SimpleInput
+ Network.Haskoin.Internals: Tx :: !Word32 -> ![TxIn] -> ![TxOut] -> !Word32 -> Tx
+ Network.Haskoin.Internals: TxIn :: !OutPoint -> !ByteString -> !Word32 -> TxIn
+ Network.Haskoin.Internals: TxOut :: !Word64 -> !ByteString -> TxOut
+ Network.Haskoin.Internals: TxSignature :: !Signature -> !SigHash -> TxSignature
+ Network.Haskoin.Internals: VarInt :: Word64 -> VarInt
+ Network.Haskoin.Internals: VarString :: ByteString -> VarString
+ Network.Haskoin.Internals: Version :: !Word32 -> !Word64 -> !Word64 -> !NetworkAddress -> !NetworkAddress -> !Word64 -> !VarString -> !Word32 -> !Bool -> Version
+ Network.Haskoin.Internals: XPrvKey :: !Word8 -> !Word32 -> !Word32 -> !ChainCode -> !PrvKey -> XPrvKey
+ Network.Haskoin.Internals: XPubKey :: !Word8 -> !Word32 -> !Word32 -> !ChainCode -> !PubKey -> XPubKey
+ Network.Haskoin.Internals: accPrvKey :: MasterKey -> KeyIndex -> Maybe AccPrvKey
+ Network.Haskoin.Internals: accPrvKeys :: MasterKey -> KeyIndex -> [(AccPrvKey, KeyIndex)]
+ Network.Haskoin.Internals: accPubKey :: MasterKey -> KeyIndex -> Maybe AccPubKey
+ Network.Haskoin.Internals: accPubKeys :: MasterKey -> KeyIndex -> [(AccPubKey, KeyIndex)]
+ Network.Haskoin.Internals: addPoint :: Point -> Point -> Point
+ Network.Haskoin.Internals: addPrvKeys :: PrvKey -> Word256 -> Maybe PrvKey
+ Network.Haskoin.Internals: addPubKeys :: PubKey -> Word256 -> Maybe PubKey
+ Network.Haskoin.Internals: addr :: AddrPubKey -> Address
+ Network.Haskoin.Internals: addrList :: Addr -> ![NetworkAddressTime]
+ Network.Haskoin.Internals: addrRecv :: Version -> !NetworkAddress
+ Network.Haskoin.Internals: addrSend :: Version -> !NetworkAddress
+ Network.Haskoin.Internals: addrToBase58 :: Address -> String
+ Network.Haskoin.Internals: alertPayload :: Alert -> !VarString
+ Network.Haskoin.Internals: alertSignature :: Alert -> !VarString
+ Network.Haskoin.Internals: anyoneCanPay :: SigHash -> !Bool
+ Network.Haskoin.Internals: base58ToAddr :: String -> Maybe Address
+ Network.Haskoin.Internals: bhNonce :: BlockHeader -> !Word32
+ Network.Haskoin.Internals: blockBits :: BlockHeader -> !Word32
+ Network.Haskoin.Internals: blockCoinbaseTx :: Block -> !CoinbaseTx
+ Network.Haskoin.Internals: blockHeader :: Block -> !BlockHeader
+ Network.Haskoin.Internals: blockTimestamp :: BlockHeader -> !Word32
+ Network.Haskoin.Internals: blockTxns :: Block -> ![Tx]
+ Network.Haskoin.Internals: blockVersion :: BlockHeader -> !Word32
+ Network.Haskoin.Internals: bloomContains :: BloomFilter -> ByteString -> Bool
+ Network.Haskoin.Internals: bloomCreate :: Int -> Double -> Word32 -> BloomFlags -> BloomFilter
+ Network.Haskoin.Internals: bloomData :: BloomFilter -> !(Seq Word8)
+ Network.Haskoin.Internals: bloomFlags :: BloomFilter -> !BloomFlags
+ Network.Haskoin.Internals: bloomHashFuncs :: BloomFilter -> !Word32
+ Network.Haskoin.Internals: bloomInsert :: BloomFilter -> ByteString -> BloomFilter
+ Network.Haskoin.Internals: bloomTweak :: BloomFilter -> !Word32
+ Network.Haskoin.Internals: buildAddrTx :: [OutPoint] -> [(String, Word64)] -> Either String Tx
+ Network.Haskoin.Internals: buildMerkleRoot :: [TxHash] -> MerkleRoot
+ Network.Haskoin.Internals: buildPartialMerkle :: [(TxHash, Bool)] -> (FlagBits, PartialMerkleTree)
+ Network.Haskoin.Internals: buildTx :: [OutPoint] -> [(ScriptOutput, Word64)] -> Either String Tx
+ Network.Haskoin.Internals: calcHash :: Int -> Int -> [TxHash] -> Word256
+ Network.Haskoin.Internals: calcTreeHeight :: Int -> Int
+ Network.Haskoin.Internals: calcTreeWidth :: Int -> Int -> Int
+ Network.Haskoin.Internals: cbData :: CoinbaseTx -> !ByteString
+ Network.Haskoin.Internals: cbHash :: CoinbaseTx -> TxHash
+ Network.Haskoin.Internals: cbInSequence :: CoinbaseTx -> !Word32
+ Network.Haskoin.Internals: cbLockTime :: CoinbaseTx -> !Word32
+ Network.Haskoin.Internals: cbOut :: CoinbaseTx -> ![TxOut]
+ Network.Haskoin.Internals: cbPrevOutput :: CoinbaseTx -> !OutPoint
+ Network.Haskoin.Internals: cbVersion :: CoinbaseTx -> !Word32
+ Network.Haskoin.Internals: checkStack :: Stack -> Bool
+ Network.Haskoin.Internals: chksum32 :: ByteString -> CheckSum32
+ Network.Haskoin.Internals: chooseCoins :: Word64 -> Word64 -> [Coin] -> Either String ([Coin], Word64)
+ Network.Haskoin.Internals: chooseMSCoins :: Word64 -> Word64 -> (Int, Int) -> [Coin] -> Either String ([Coin], Word64)
+ Network.Haskoin.Internals: class BigWordMod a
+ Network.Haskoin.Internals: coinOutPoint :: Coin -> !OutPoint
+ Network.Haskoin.Internals: coinRedeem :: Coin -> !(Maybe RedeemScript)
+ Network.Haskoin.Internals: coinScript :: Coin -> !ScriptOutput
+ Network.Haskoin.Internals: coinValue :: Coin -> !Word64
+ Network.Haskoin.Internals: curveA :: FieldP
+ Network.Haskoin.Internals: curveB :: FieldP
+ Network.Haskoin.Internals: curveG :: Point
+ Network.Haskoin.Internals: curveN :: Integer
+ Network.Haskoin.Internals: curveP :: Integer
+ Network.Haskoin.Internals: cycleIndex :: Word32 -> [Word32]
+ Network.Haskoin.Internals: cycleIndex' :: Word32 -> [Word32]
+ Network.Haskoin.Internals: data Addr
+ Network.Haskoin.Internals: data Address
+ Network.Haskoin.Internals: data Alert
+ Network.Haskoin.Internals: data Block
+ Network.Haskoin.Internals: data BlockHeader
+ Network.Haskoin.Internals: data BloomFilter
+ Network.Haskoin.Internals: data BloomFlags
+ Network.Haskoin.Internals: data Coin
+ Network.Haskoin.Internals: data CoinbaseTx
+ Network.Haskoin.Internals: data GetBlocks
+ Network.Haskoin.Internals: data GetData
+ Network.Haskoin.Internals: data GetHeaders
+ Network.Haskoin.Internals: data Headers
+ Network.Haskoin.Internals: data Inv
+ Network.Haskoin.Internals: data InvType
+ Network.Haskoin.Internals: data InvVector
+ Network.Haskoin.Internals: data MerkleBlock
+ Network.Haskoin.Internals: data Message
+ Network.Haskoin.Internals: data MessageCommand
+ Network.Haskoin.Internals: data MessageHeader
+ Network.Haskoin.Internals: data NetworkAddress
+ Network.Haskoin.Internals: data NotFound
+ Network.Haskoin.Internals: data OutPoint
+ Network.Haskoin.Internals: data Point
+ Network.Haskoin.Internals: data Program
+ Network.Haskoin.Internals: data PrvKey
+ Network.Haskoin.Internals: data PubKey
+ Network.Haskoin.Internals: data PushDataType
+ Network.Haskoin.Internals: data Reject
+ Network.Haskoin.Internals: data RejectCode
+ Network.Haskoin.Internals: data Script
+ Network.Haskoin.Internals: data ScriptInput
+ Network.Haskoin.Internals: data ScriptOp
+ Network.Haskoin.Internals: data ScriptOutput
+ Network.Haskoin.Internals: data SigHash
+ Network.Haskoin.Internals: data SigInput
+ Network.Haskoin.Internals: data Signature
+ Network.Haskoin.Internals: data SimpleInput
+ Network.Haskoin.Internals: data Tx
+ Network.Haskoin.Internals: data TxIn
+ Network.Haskoin.Internals: data TxOut
+ Network.Haskoin.Internals: data TxSignature
+ Network.Haskoin.Internals: data Version
+ Network.Haskoin.Internals: data XPrvKey
+ Network.Haskoin.Internals: data XPubKey
+ Network.Haskoin.Internals: decodeBase58 :: ByteString -> Maybe ByteString
+ Network.Haskoin.Internals: decodeBase58Check :: ByteString -> Maybe ByteString
+ Network.Haskoin.Internals: decodeBlockHashLE :: String -> Maybe BlockHash
+ Network.Haskoin.Internals: decodeBool :: StackValue -> Bool
+ Network.Haskoin.Internals: decodeCanonicalSig :: ByteString -> Either String TxSignature
+ Network.Haskoin.Internals: decodeCompact :: Word32 -> Integer
+ Network.Haskoin.Internals: decodeInput :: Script -> Either String ScriptInput
+ Network.Haskoin.Internals: decodeInputBS :: ByteString -> Either String ScriptInput
+ Network.Haskoin.Internals: decodeInt :: StackValue -> Maybe Int64
+ Network.Haskoin.Internals: decodeOutput :: Script -> Either String ScriptOutput
+ Network.Haskoin.Internals: decodeOutputBS :: ByteString -> Either String ScriptOutput
+ Network.Haskoin.Internals: decodeSig :: ByteString -> Either String TxSignature
+ Network.Haskoin.Internals: decodeTxHashLE :: String -> Maybe TxHash
+ Network.Haskoin.Internals: derivePubKey :: PrvKey -> PubKey
+ Network.Haskoin.Internals: deriveXPubKey :: XPrvKey -> XPubKey
+ Network.Haskoin.Internals: detSignInput :: Tx -> Int -> SigInput -> PrvKey -> Either String (Tx, Bool)
+ Network.Haskoin.Internals: detSignMsg :: Word256 -> PrvKey -> Signature
+ Network.Haskoin.Internals: detSignTx :: Tx -> [SigInput] -> [PrvKey] -> Either String (Tx, Bool)
+ Network.Haskoin.Internals: devRandom :: Int -> IO ByteString
+ Network.Haskoin.Internals: devURandom :: Int -> IO ByteString
+ Network.Haskoin.Internals: doubleHash256 :: ByteString -> Word256
+ Network.Haskoin.Internals: doubleHash256BS :: ByteString -> ByteString
+ Network.Haskoin.Internals: doublePoint :: Point -> Point
+ Network.Haskoin.Internals: dumpScript :: [ScriptOp] -> String
+ Network.Haskoin.Internals: dumpStack :: Stack -> String
+ Network.Haskoin.Internals: encodeBase58 :: ByteString -> ByteString
+ Network.Haskoin.Internals: encodeBase58Check :: ByteString -> ByteString
+ Network.Haskoin.Internals: encodeBlockHashLE :: BlockHash -> String
+ Network.Haskoin.Internals: encodeBool :: Bool -> StackValue
+ Network.Haskoin.Internals: encodeCompact :: Integer -> Word32
+ Network.Haskoin.Internals: encodeInput :: ScriptInput -> Script
+ Network.Haskoin.Internals: encodeInputBS :: ScriptInput -> ByteString
+ Network.Haskoin.Internals: encodeInt :: Int64 -> StackValue
+ Network.Haskoin.Internals: encodeOutput :: ScriptOutput -> Script
+ Network.Haskoin.Internals: encodeOutputBS :: ScriptOutput -> ByteString
+ Network.Haskoin.Internals: encodeSig :: TxSignature -> ByteString
+ Network.Haskoin.Internals: encodeSigHash32 :: SigHash -> ByteString
+ Network.Haskoin.Internals: encodeTxHashLE :: TxHash -> String
+ Network.Haskoin.Internals: evalScript :: Script -> Script -> SigCheck -> Bool
+ Network.Haskoin.Internals: execScript :: Script -> Script -> SigCheck -> Either EvalError Program
+ Network.Haskoin.Internals: extAddr :: AccPubKey -> KeyIndex -> Maybe Address
+ Network.Haskoin.Internals: extAddrs :: AccPubKey -> KeyIndex -> [(Address, KeyIndex)]
+ Network.Haskoin.Internals: extAddrs' :: AccPubKey -> KeyIndex -> [(Address, KeyIndex)]
+ Network.Haskoin.Internals: extMulSigAddr :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> Maybe Address
+ Network.Haskoin.Internals: extMulSigAddrs :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> [(Address, KeyIndex)]
+ Network.Haskoin.Internals: extMulSigKey :: AccPubKey -> [XPubKey] -> KeyIndex -> Maybe [AddrPubKey]
+ Network.Haskoin.Internals: extMulSigKeys :: AccPubKey -> [XPubKey] -> KeyIndex -> [([AddrPubKey], KeyIndex)]
+ Network.Haskoin.Internals: extPrvKey :: AccPrvKey -> KeyIndex -> Maybe AddrPrvKey
+ Network.Haskoin.Internals: extPrvKeys :: AccPrvKey -> KeyIndex -> [(AddrPrvKey, KeyIndex)]
+ Network.Haskoin.Internals: extPubKey :: AccPubKey -> KeyIndex -> Maybe AddrPubKey
+ Network.Haskoin.Internals: extPubKeys :: AccPubKey -> KeyIndex -> [(AddrPubKey, KeyIndex)]
+ Network.Haskoin.Internals: extendedModGCD :: Integer -> Integer -> Integer -> (Integer, Integer)
+ Network.Haskoin.Internals: extractMatches :: FlagBits -> PartialMerkleTree -> Int -> Either String (MerkleRoot, [TxHash])
+ Network.Haskoin.Internals: fromMnemonic :: Mnemonic -> Either String Entropy
+ Network.Haskoin.Internals: fromPrvKey :: PrvKey -> Integer
+ Network.Haskoin.Internals: fromWIF :: String -> Maybe PrvKey
+ Network.Haskoin.Internals: genPrvKey :: Monad m => SecretT m PrvKey
+ Network.Haskoin.Internals: getAccPrvKey :: AccPrvKey -> XPrvKey
+ Network.Haskoin.Internals: getAccPubKey :: AccPubKey -> XPubKey
+ Network.Haskoin.Internals: getAddrHash :: Address -> Word160
+ Network.Haskoin.Internals: getAddrPrvKey :: AddrPrvKey -> XPrvKey
+ Network.Haskoin.Internals: getAddrPubKey :: AddrPubKey -> XPubKey
+ Network.Haskoin.Internals: getAffine :: Point -> Maybe (FieldP, FieldP)
+ Network.Haskoin.Internals: getBigWordInteger :: BigWord n -> Integer
+ Network.Haskoin.Internals: getBits :: Int -> ByteString -> ByteString
+ Network.Haskoin.Internals: getBlocksHashStop :: GetBlocks -> !BlockHash
+ Network.Haskoin.Internals: getBlocksLocator :: GetBlocks -> !BlockLocator
+ Network.Haskoin.Internals: getBlocksVersion :: GetBlocks -> !Word32
+ Network.Haskoin.Internals: getBloomFilter :: FilterLoad -> BloomFilter
+ Network.Haskoin.Internals: getDataList :: GetData -> ![InvVector]
+ Network.Haskoin.Internals: getFee :: Word64 -> Int -> Word64
+ Network.Haskoin.Internals: getFilterData :: FilterAdd -> ByteString
+ Network.Haskoin.Internals: getHeadersBL :: GetHeaders -> !BlockLocator
+ Network.Haskoin.Internals: getHeadersHashStop :: GetHeaders -> !BlockHash
+ Network.Haskoin.Internals: getHeadersVersion :: GetHeaders -> !Word32
+ Network.Haskoin.Internals: getInputKey :: SimpleInput -> !PubKey
+ Network.Haskoin.Internals: getInputMulSigKeys :: SimpleInput -> ![TxSignature]
+ Network.Haskoin.Internals: getInputSig :: SimpleInput -> !TxSignature
+ Network.Haskoin.Internals: getMSFee :: Word64 -> (Int, Int) -> Int -> Word64
+ Network.Haskoin.Internals: getOutputAddress :: ScriptOutput -> !Address
+ Network.Haskoin.Internals: getOutputMulSigKeys :: ScriptOutput -> ![PubKey]
+ Network.Haskoin.Internals: getOutputMulSigRequired :: ScriptOutput -> !Int
+ Network.Haskoin.Internals: getOutputPubKey :: ScriptOutput -> !PubKey
+ Network.Haskoin.Internals: getPrvKey :: Get PrvKey
+ Network.Haskoin.Internals: getPrvKeyU :: Get PrvKey
+ Network.Haskoin.Internals: getRegularInput :: ScriptInput -> SimpleInput
+ Network.Haskoin.Internals: getScriptHashInput :: ScriptInput -> SimpleInput
+ Network.Haskoin.Internals: getScriptHashRedeem :: ScriptInput -> RedeemScript
+ Network.Haskoin.Internals: getSigCode :: SigHash -> !Word8
+ Network.Haskoin.Internals: getVarInt :: VarInt -> Word64
+ Network.Haskoin.Internals: getVarString :: VarString -> ByteString
+ Network.Haskoin.Internals: getX :: Point -> Maybe FieldP
+ Network.Haskoin.Internals: getY :: Point -> Maybe FieldP
+ Network.Haskoin.Internals: guessTxSize :: Int -> [(Int, Int)] -> Int -> Int -> Int
+ Network.Haskoin.Internals: hash160 :: ByteString -> Word160
+ Network.Haskoin.Internals: hash160BS :: ByteString -> ByteString
+ Network.Haskoin.Internals: hash256 :: ByteString -> Word256
+ Network.Haskoin.Internals: hash256BS :: ByteString -> ByteString
+ Network.Haskoin.Internals: hash512 :: ByteString -> Word512
+ Network.Haskoin.Internals: hash512BS :: ByteString -> ByteString
+ Network.Haskoin.Internals: hashSha1 :: ByteString -> Word160
+ Network.Haskoin.Internals: hashSha1BS :: ByteString -> ByteString
+ Network.Haskoin.Internals: headChecksum :: MessageHeader -> !CheckSum32
+ Network.Haskoin.Internals: headCmd :: MessageHeader -> !MessageCommand
+ Network.Haskoin.Internals: headMagic :: MessageHeader -> !Word32
+ Network.Haskoin.Internals: headPayloadSize :: MessageHeader -> !Word32
+ Network.Haskoin.Internals: headerHash :: BlockHeader -> BlockHash
+ Network.Haskoin.Internals: headersList :: Headers -> ![BlockHeaderCount]
+ Network.Haskoin.Internals: hmac256 :: ByteString -> ByteString -> Word256
+ Network.Haskoin.Internals: hmac256BS :: ByteString -> ByteString -> ByteString
+ Network.Haskoin.Internals: hmac512 :: ByteString -> ByteString -> Word512
+ Network.Haskoin.Internals: hmac512BS :: ByteString -> ByteString -> ByteString
+ Network.Haskoin.Internals: hmacDRBGGen :: WorkingState -> Word16 -> AdditionalInput -> (WorkingState, Maybe ByteString)
+ Network.Haskoin.Internals: hmacDRBGNew :: EntropyInput -> Nonce -> PersString -> WorkingState
+ Network.Haskoin.Internals: hmacDRBGRsd :: WorkingState -> EntropyInput -> AdditionalInput -> WorkingState
+ Network.Haskoin.Internals: hmacDRBGUpd :: ProvidedData -> ByteString -> ByteString -> (ByteString, ByteString)
+ Network.Haskoin.Internals: intAddr :: AccPubKey -> KeyIndex -> Maybe Address
+ Network.Haskoin.Internals: intAddrs :: AccPubKey -> KeyIndex -> [(Address, KeyIndex)]
+ Network.Haskoin.Internals: intAddrs' :: AccPubKey -> KeyIndex -> [(Address, KeyIndex)]
+ Network.Haskoin.Internals: intMulSigAddr :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> Maybe Address
+ Network.Haskoin.Internals: intMulSigAddrs :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> [(Address, KeyIndex)]
+ Network.Haskoin.Internals: intMulSigKey :: AccPubKey -> [XPubKey] -> KeyIndex -> Maybe [AddrPubKey]
+ Network.Haskoin.Internals: intMulSigKeys :: AccPubKey -> [XPubKey] -> KeyIndex -> [([AddrPubKey], KeyIndex)]
+ Network.Haskoin.Internals: intPrvKey :: AccPrvKey -> KeyIndex -> Maybe AddrPrvKey
+ Network.Haskoin.Internals: intPrvKeys :: AccPrvKey -> KeyIndex -> [(AddrPrvKey, KeyIndex)]
+ Network.Haskoin.Internals: intPubKey :: AccPubKey -> KeyIndex -> Maybe AddrPubKey
+ Network.Haskoin.Internals: intPubKeys :: AccPubKey -> KeyIndex -> [(AddrPubKey, KeyIndex)]
+ Network.Haskoin.Internals: intToScriptOp :: Int -> ScriptOp
+ Network.Haskoin.Internals: integerA :: Integer
+ Network.Haskoin.Internals: integerB :: Integer
+ Network.Haskoin.Internals: invHash :: InvVector -> !Word256
+ Network.Haskoin.Internals: invList :: Inv -> ![InvVector]
+ Network.Haskoin.Internals: invType :: InvVector -> !InvType
+ Network.Haskoin.Internals: inverseN :: FieldN -> FieldN
+ Network.Haskoin.Internals: inverseP :: FieldP -> FieldP
+ Network.Haskoin.Internals: isBloomEmpty :: BloomFilter -> Bool
+ Network.Haskoin.Internals: isBloomFull :: BloomFilter -> Bool
+ Network.Haskoin.Internals: isBloomValid :: BloomFilter -> Bool
+ Network.Haskoin.Internals: isCanonicalHalfOrder :: Signature -> Bool
+ Network.Haskoin.Internals: isInfPoint :: Point -> Bool
+ Network.Haskoin.Internals: isIntegerValidKey :: Integer -> Bool
+ Network.Haskoin.Internals: isPayMulSig :: ScriptOutput -> Bool
+ Network.Haskoin.Internals: isPayPK :: ScriptOutput -> Bool
+ Network.Haskoin.Internals: isPayPKHash :: ScriptOutput -> Bool
+ Network.Haskoin.Internals: isPayScriptHash :: ScriptOutput -> Bool
+ Network.Haskoin.Internals: isPrvKeyU :: PrvKey -> Bool
+ Network.Haskoin.Internals: isPubKeyU :: PubKey -> Bool
+ Network.Haskoin.Internals: isPushOp :: ScriptOp -> Bool
+ Network.Haskoin.Internals: isScriptHashInput :: ScriptInput -> Bool
+ Network.Haskoin.Internals: isSigAll :: SigHash -> Bool
+ Network.Haskoin.Internals: isSigNone :: SigHash -> Bool
+ Network.Haskoin.Internals: isSigSingle :: SigHash -> Bool
+ Network.Haskoin.Internals: isSigUnknown :: SigHash -> Bool
+ Network.Haskoin.Internals: isSpendMulSig :: ScriptInput -> Bool
+ Network.Haskoin.Internals: isSpendPK :: ScriptInput -> Bool
+ Network.Haskoin.Internals: isSpendPKHash :: ScriptInput -> Bool
+ Network.Haskoin.Internals: isValidPrvKey :: Integer -> Bool
+ Network.Haskoin.Internals: isValidPubKey :: PubKey -> Bool
+ Network.Haskoin.Internals: join512 :: (Word256, Word256) -> Word512
+ Network.Haskoin.Internals: loadMasterKey :: XPrvKey -> Maybe MasterKey
+ Network.Haskoin.Internals: loadPrvAcc :: XPrvKey -> Maybe AccPrvKey
+ Network.Haskoin.Internals: loadPubAcc :: XPubKey -> Maybe AccPubKey
+ Network.Haskoin.Internals: mFlags :: MerkleBlock -> ![Bool]
+ Network.Haskoin.Internals: mHashes :: MerkleBlock -> ![Word256]
+ Network.Haskoin.Internals: makeInfPoint :: Point
+ Network.Haskoin.Internals: makeMasterKey :: ByteString -> Maybe MasterKey
+ Network.Haskoin.Internals: makePoint :: FieldP -> FieldP -> Maybe Point
+ Network.Haskoin.Internals: makePrvKey :: Integer -> Maybe PrvKey
+ Network.Haskoin.Internals: makePrvKeyU :: Integer -> Maybe PrvKey
+ Network.Haskoin.Internals: makeXPrvKey :: ByteString -> Maybe XPrvKey
+ Network.Haskoin.Internals: masterKey :: MasterKey -> XPrvKey
+ Network.Haskoin.Internals: merkleHeader :: MerkleBlock -> !BlockHeader
+ Network.Haskoin.Internals: merkleRoot :: BlockHeader -> !Word256
+ Network.Haskoin.Internals: merkleTotalTxns :: MerkleBlock -> !Word32
+ Network.Haskoin.Internals: mnemonicToSeed :: Passphrase -> Mnemonic -> Either String Seed
+ Network.Haskoin.Internals: mulInverse :: Integer -> Integer -> Integer
+ Network.Haskoin.Internals: mulPoint :: FieldN -> Point -> Point
+ Network.Haskoin.Internals: mulSigSubKey :: [XPubKey] -> Word32 -> Maybe [XPubKey]
+ Network.Haskoin.Internals: mulSigSubKeys :: [XPubKey] -> Word32 -> [([XPubKey], Word32)]
+ Network.Haskoin.Internals: murmurHash3 :: Word32 -> ByteString -> Word32
+ Network.Haskoin.Internals: naAddress :: NetworkAddress -> !(Word64, Word64)
+ Network.Haskoin.Internals: naPort :: NetworkAddress -> !Word16
+ Network.Haskoin.Internals: naServices :: NetworkAddress -> !Word64
+ Network.Haskoin.Internals: newtype AccPrvKey
+ Network.Haskoin.Internals: newtype AccPubKey
+ Network.Haskoin.Internals: newtype AddrPrvKey
+ Network.Haskoin.Internals: newtype AddrPubKey
+ Network.Haskoin.Internals: newtype BigWord n
+ Network.Haskoin.Internals: newtype FilterAdd
+ Network.Haskoin.Internals: newtype FilterLoad
+ Network.Haskoin.Internals: newtype MasterKey
+ Network.Haskoin.Internals: newtype Ping
+ Network.Haskoin.Internals: newtype Pong
+ Network.Haskoin.Internals: newtype VarInt
+ Network.Haskoin.Internals: newtype VarString
+ Network.Haskoin.Internals: notFoundList :: NotFound -> ![InvVector]
+ Network.Haskoin.Internals: opPushData :: ByteString -> ScriptOp
+ Network.Haskoin.Internals: outPointHash :: OutPoint -> !TxHash
+ Network.Haskoin.Internals: outPointIndex :: OutPoint -> !Word32
+ Network.Haskoin.Internals: outValue :: TxOut -> !Word64
+ Network.Haskoin.Internals: pairG :: (Integer, Integer)
+ Network.Haskoin.Internals: pingNonce :: Ping -> Word64
+ Network.Haskoin.Internals: pongNonce :: Pong -> Word64
+ Network.Haskoin.Internals: prevBlock :: BlockHeader -> !BlockHash
+ Network.Haskoin.Internals: prevOutput :: TxIn -> !OutPoint
+ Network.Haskoin.Internals: primeSubKey :: XPrvKey -> Word32 -> Maybe XPrvKey
+ Network.Haskoin.Internals: primeSubKeys :: XPrvKey -> Word32 -> [(XPrvKey, Word32)]
+ Network.Haskoin.Internals: prvKeyFieldN :: PrvKey -> !FieldN
+ Network.Haskoin.Internals: prvSubKey :: XPrvKey -> Word32 -> Maybe XPrvKey
+ Network.Haskoin.Internals: prvSubKeys :: XPrvKey -> Word32 -> [(XPrvKey, Word32)]
+ Network.Haskoin.Internals: pubKeyAddr :: PubKey -> Address
+ Network.Haskoin.Internals: pubKeyPoint :: PubKey -> !Point
+ Network.Haskoin.Internals: pubSubKey :: XPubKey -> Word32 -> Maybe XPubKey
+ Network.Haskoin.Internals: pubSubKeys :: XPubKey -> Word32 -> [(XPubKey, Word32)]
+ Network.Haskoin.Internals: putPrvKey :: PrvKey -> Put
+ Network.Haskoin.Internals: quadraticResidue :: FieldP -> [FieldP]
+ Network.Haskoin.Internals: rBitSize :: BigWordMod a => BigWord a -> Int
+ Network.Haskoin.Internals: rFromInteger :: BigWordMod a => Integer -> BigWord a
+ Network.Haskoin.Internals: reject :: MessageCommand -> RejectCode -> String -> Reject
+ Network.Haskoin.Internals: rejectCode :: Reject -> !RejectCode
+ Network.Haskoin.Internals: rejectMessage :: Reject -> !MessageCommand
+ Network.Haskoin.Internals: rejectReason :: Reject -> !VarString
+ Network.Haskoin.Internals: relay :: Version -> !Bool
+ Network.Haskoin.Internals: runStack :: Program -> Stack
+ Network.Haskoin.Internals: scriptAddr :: ScriptOutput -> Address
+ Network.Haskoin.Internals: scriptInput :: TxIn -> !ByteString
+ Network.Haskoin.Internals: scriptOpToInt :: ScriptOp -> Either String Int
+ Network.Haskoin.Internals: scriptOps :: Script -> ![ScriptOp]
+ Network.Haskoin.Internals: scriptOutput :: TxOut -> !ByteString
+ Network.Haskoin.Internals: scriptRecipient :: Script -> Either String Address
+ Network.Haskoin.Internals: scriptSender :: Script -> Either String Address
+ Network.Haskoin.Internals: services :: Version -> !Word64
+ Network.Haskoin.Internals: shamirsTrick :: FieldN -> Point -> FieldN -> Point -> Point
+ Network.Haskoin.Internals: sigDataOP :: SigInput -> !OutPoint
+ Network.Haskoin.Internals: sigDataOut :: SigInput -> !ScriptOutput
+ Network.Haskoin.Internals: sigDataRedeem :: SigInput -> !(Maybe RedeemScript)
+ Network.Haskoin.Internals: sigDataSH :: SigInput -> !SigHash
+ Network.Haskoin.Internals: sigHashType :: TxSignature -> !SigHash
+ Network.Haskoin.Internals: sigR :: Signature -> !FieldN
+ Network.Haskoin.Internals: sigS :: Signature -> !FieldN
+ Network.Haskoin.Internals: signInput :: Monad m => Tx -> Int -> SigInput -> PrvKey -> EitherT String (SecretT m) (Tx, Bool)
+ Network.Haskoin.Internals: signMsg :: Monad m => Word256 -> PrvKey -> SecretT m Signature
+ Network.Haskoin.Internals: signTx :: Monad m => Tx -> [SigInput] -> [PrvKey] -> EitherT String (SecretT m) (Tx, Bool)
+ Network.Haskoin.Internals: sortMulSig :: ScriptOutput -> ScriptOutput
+ Network.Haskoin.Internals: split512 :: Word512 -> (Word256, Word256)
+ Network.Haskoin.Internals: startHeight :: Version -> !Word32
+ Network.Haskoin.Internals: timestamp :: Version -> !Word64
+ Network.Haskoin.Internals: toMnemonic :: Entropy -> Either String Mnemonic
+ Network.Haskoin.Internals: toWIF :: PrvKey -> String
+ Network.Haskoin.Internals: txHash :: Tx -> TxHash
+ Network.Haskoin.Internals: txIn :: Tx -> ![TxIn]
+ Network.Haskoin.Internals: txInSequence :: TxIn -> !Word32
+ Network.Haskoin.Internals: txLockTime :: Tx -> !Word32
+ Network.Haskoin.Internals: txOut :: Tx -> ![TxOut]
+ Network.Haskoin.Internals: txSigHash :: Tx -> Script -> Int -> SigHash -> Word256
+ Network.Haskoin.Internals: txSignature :: TxSignature -> !Signature
+ Network.Haskoin.Internals: txVersion :: Tx -> !Word32
+ Network.Haskoin.Internals: type BlockHash = BigWord Mod256Block
+ Network.Haskoin.Internals: type BlockHeaderCount = (BlockHeader, VarInt)
+ Network.Haskoin.Internals: type BlockLocator = [BlockHash]
+ Network.Haskoin.Internals: type ChainCode = Word256
+ Network.Haskoin.Internals: type CheckSum32 = Word32
+ Network.Haskoin.Internals: type Entropy = ByteString
+ Network.Haskoin.Internals: type FieldN = BigWord ModN
+ Network.Haskoin.Internals: type FieldP = BigWord ModP
+ Network.Haskoin.Internals: type KeyIndex = Word32
+ Network.Haskoin.Internals: type Mnemonic = String
+ Network.Haskoin.Internals: type NetworkAddressTime = (Word32, NetworkAddress)
+ Network.Haskoin.Internals: type Passphrase = String
+ Network.Haskoin.Internals: type RedeemScript = ScriptOutput
+ Network.Haskoin.Internals: type SecretT m = StateT (SecretState m) m
+ Network.Haskoin.Internals: type Seed = ByteString
+ Network.Haskoin.Internals: type SigCheck = [ScriptOp] -> TxSignature -> PubKey -> Bool
+ Network.Haskoin.Internals: type Stack = [StackValue]
+ Network.Haskoin.Internals: type TxHash = BigWord Mod256Tx
+ Network.Haskoin.Internals: type Word128 = BigWord Mod128
+ Network.Haskoin.Internals: type Word160 = BigWord Mod160
+ Network.Haskoin.Internals: type Word256 = BigWord Mod256
+ Network.Haskoin.Internals: type Word512 = BigWord Mod512
+ Network.Haskoin.Internals: type WorkingState = (ByteString, ByteString, Word16)
+ Network.Haskoin.Internals: unsafeSignMsg :: Word256 -> FieldN -> (FieldN, Point) -> Maybe Signature
+ Network.Haskoin.Internals: userAgent :: Version -> !VarString
+ Network.Haskoin.Internals: validatePoint :: Point -> Bool
+ Network.Haskoin.Internals: verNonce :: Version -> !Word64
+ Network.Haskoin.Internals: verifySig :: Word256 -> Signature -> PubKey -> Bool
+ Network.Haskoin.Internals: verifySpend :: Tx -> Int -> Script -> Bool
+ Network.Haskoin.Internals: verifyStdInput :: Tx -> Int -> ScriptOutput -> Bool
+ Network.Haskoin.Internals: verifyStdTx :: Tx -> [(ScriptOutput, OutPoint)] -> Bool
+ Network.Haskoin.Internals: version :: Version -> !Word32
+ Network.Haskoin.Internals: withSource :: Monad m => (Int -> m ByteString) -> SecretT m a -> m a
+ Network.Haskoin.Internals: xPrvChain :: XPrvKey -> !ChainCode
+ Network.Haskoin.Internals: xPrvChild :: XPrvKey -> Word32
+ Network.Haskoin.Internals: xPrvDepth :: XPrvKey -> !Word8
+ Network.Haskoin.Internals: xPrvExport :: XPrvKey -> String
+ Network.Haskoin.Internals: xPrvFP :: XPrvKey -> Word32
+ Network.Haskoin.Internals: xPrvID :: XPrvKey -> Word160
+ Network.Haskoin.Internals: xPrvImport :: String -> Maybe XPrvKey
+ Network.Haskoin.Internals: xPrvIndex :: XPrvKey -> !Word32
+ Network.Haskoin.Internals: xPrvIsPrime :: XPrvKey -> Bool
+ Network.Haskoin.Internals: xPrvKey :: XPrvKey -> !PrvKey
+ Network.Haskoin.Internals: xPrvParent :: XPrvKey -> !Word32
+ Network.Haskoin.Internals: xPrvWIF :: XPrvKey -> String
+ Network.Haskoin.Internals: xPubAddr :: XPubKey -> Address
+ Network.Haskoin.Internals: xPubChain :: XPubKey -> !ChainCode
+ Network.Haskoin.Internals: xPubChild :: XPubKey -> Word32
+ Network.Haskoin.Internals: xPubDepth :: XPubKey -> !Word8
+ Network.Haskoin.Internals: xPubExport :: XPubKey -> String
+ Network.Haskoin.Internals: xPubFP :: XPubKey -> Word32
+ Network.Haskoin.Internals: xPubID :: XPubKey -> Word160
+ Network.Haskoin.Internals: xPubImport :: String -> Maybe XPubKey
+ Network.Haskoin.Internals: xPubIndex :: XPubKey -> !Word32
+ Network.Haskoin.Internals: xPubIsPrime :: XPubKey -> Bool
+ Network.Haskoin.Internals: xPubKey :: XPubKey -> !PubKey
+ Network.Haskoin.Internals: xPubParent :: XPubKey -> !Word32
+ Network.Haskoin.Protocol: MCMerkleBlock :: MessageCommand
+ Network.Haskoin.Protocol: MCReject :: MessageCommand
+ Network.Haskoin.Protocol: MMerkleBlock :: !MerkleBlock -> Message
+ Network.Haskoin.Protocol: MReject :: Reject -> Message
+ Network.Haskoin.Protocol: Reject :: !MessageCommand -> !RejectCode -> !VarString -> Reject
+ Network.Haskoin.Protocol: RejectCheckpoint :: RejectCode
+ Network.Haskoin.Protocol: RejectDuplicate :: RejectCode
+ Network.Haskoin.Protocol: RejectDust :: RejectCode
+ Network.Haskoin.Protocol: RejectInsufficientFee :: RejectCode
+ Network.Haskoin.Protocol: RejectInvalid :: RejectCode
+ Network.Haskoin.Protocol: RejectMalformed :: RejectCode
+ Network.Haskoin.Protocol: RejectNonStandard :: RejectCode
+ Network.Haskoin.Protocol: RejectObsolete :: RejectCode
+ Network.Haskoin.Protocol: data Reject
+ Network.Haskoin.Protocol: data RejectCode
+ Network.Haskoin.Protocol: reject :: MessageCommand -> RejectCode -> String -> Reject
+ Network.Haskoin.Protocol: rejectCode :: Reject -> !RejectCode
+ Network.Haskoin.Protocol: rejectMessage :: Reject -> !MessageCommand
+ Network.Haskoin.Protocol: rejectReason :: Reject -> !VarString
+ Network.Haskoin.Script: OPCODE :: PushDataType
+ Network.Haskoin.Script: OPDATA1 :: PushDataType
+ Network.Haskoin.Script: OPDATA2 :: PushDataType
+ Network.Haskoin.Script: OPDATA4 :: PushDataType
+ Network.Haskoin.Script: OP_0 :: ScriptOp
+ Network.Haskoin.Script: OP_0NOTEQUAL :: ScriptOp
+ Network.Haskoin.Script: OP_1 :: ScriptOp
+ Network.Haskoin.Script: OP_10 :: ScriptOp
+ Network.Haskoin.Script: OP_11 :: ScriptOp
+ Network.Haskoin.Script: OP_12 :: ScriptOp
+ Network.Haskoin.Script: OP_13 :: ScriptOp
+ Network.Haskoin.Script: OP_14 :: ScriptOp
+ Network.Haskoin.Script: OP_15 :: ScriptOp
+ Network.Haskoin.Script: OP_16 :: ScriptOp
+ Network.Haskoin.Script: OP_1ADD :: ScriptOp
+ Network.Haskoin.Script: OP_1NEGATE :: ScriptOp
+ Network.Haskoin.Script: OP_1SUB :: ScriptOp
+ Network.Haskoin.Script: OP_2 :: ScriptOp
+ Network.Haskoin.Script: OP_2DIV :: ScriptOp
+ Network.Haskoin.Script: OP_2DROP :: ScriptOp
+ Network.Haskoin.Script: OP_2DUP :: ScriptOp
+ Network.Haskoin.Script: OP_2MUL :: ScriptOp
+ Network.Haskoin.Script: OP_2OVER :: ScriptOp
+ Network.Haskoin.Script: OP_2ROT :: ScriptOp
+ Network.Haskoin.Script: OP_2SWAP :: ScriptOp
+ Network.Haskoin.Script: OP_3 :: ScriptOp
+ Network.Haskoin.Script: OP_3DUP :: ScriptOp
+ Network.Haskoin.Script: OP_4 :: ScriptOp
+ Network.Haskoin.Script: OP_5 :: ScriptOp
+ Network.Haskoin.Script: OP_6 :: ScriptOp
+ Network.Haskoin.Script: OP_7 :: ScriptOp
+ Network.Haskoin.Script: OP_8 :: ScriptOp
+ Network.Haskoin.Script: OP_9 :: ScriptOp
+ Network.Haskoin.Script: OP_ABS :: ScriptOp
+ Network.Haskoin.Script: OP_ADD :: ScriptOp
+ Network.Haskoin.Script: OP_AND :: ScriptOp
+ Network.Haskoin.Script: OP_BOOLAND :: ScriptOp
+ Network.Haskoin.Script: OP_BOOLOR :: ScriptOp
+ Network.Haskoin.Script: OP_CAT :: ScriptOp
+ Network.Haskoin.Script: OP_CHECKMULTISIG :: ScriptOp
+ Network.Haskoin.Script: OP_CHECKMULTISIGVERIFY :: ScriptOp
+ Network.Haskoin.Script: OP_CHECKSIG :: ScriptOp
+ Network.Haskoin.Script: OP_CHECKSIGVERIFY :: ScriptOp
+ Network.Haskoin.Script: OP_CODESEPARATOR :: ScriptOp
+ Network.Haskoin.Script: OP_DEPTH :: ScriptOp
+ Network.Haskoin.Script: OP_DIV :: ScriptOp
+ Network.Haskoin.Script: OP_DROP :: ScriptOp
+ Network.Haskoin.Script: OP_DUP :: ScriptOp
+ Network.Haskoin.Script: OP_ELSE :: ScriptOp
+ Network.Haskoin.Script: OP_ENDIF :: ScriptOp
+ Network.Haskoin.Script: OP_EQUAL :: ScriptOp
+ Network.Haskoin.Script: OP_EQUALVERIFY :: ScriptOp
+ Network.Haskoin.Script: OP_FROMALTSTACK :: ScriptOp
+ Network.Haskoin.Script: OP_GREATERTHAN :: ScriptOp
+ Network.Haskoin.Script: OP_GREATERTHANOREQUAL :: ScriptOp
+ Network.Haskoin.Script: OP_HASH160 :: ScriptOp
+ Network.Haskoin.Script: OP_HASH256 :: ScriptOp
+ Network.Haskoin.Script: OP_IF :: ScriptOp
+ Network.Haskoin.Script: OP_IFDUP :: ScriptOp
+ Network.Haskoin.Script: OP_INVALIDOPCODE :: !Word8 -> ScriptOp
+ Network.Haskoin.Script: OP_INVERT :: ScriptOp
+ Network.Haskoin.Script: OP_LEFT :: ScriptOp
+ Network.Haskoin.Script: OP_LESSTHAN :: ScriptOp
+ Network.Haskoin.Script: OP_LESSTHANOREQUAL :: ScriptOp
+ Network.Haskoin.Script: OP_LSHIFT :: ScriptOp
+ Network.Haskoin.Script: OP_MAX :: ScriptOp
+ Network.Haskoin.Script: OP_MIN :: ScriptOp
+ Network.Haskoin.Script: OP_MOD :: ScriptOp
+ Network.Haskoin.Script: OP_MUL :: ScriptOp
+ Network.Haskoin.Script: OP_NEGATE :: ScriptOp
+ Network.Haskoin.Script: OP_NIP :: ScriptOp
+ Network.Haskoin.Script: OP_NOP :: ScriptOp
+ Network.Haskoin.Script: OP_NOP1 :: ScriptOp
+ Network.Haskoin.Script: OP_NOP10 :: ScriptOp
+ Network.Haskoin.Script: OP_NOP2 :: ScriptOp
+ Network.Haskoin.Script: OP_NOP3 :: ScriptOp
+ Network.Haskoin.Script: OP_NOP4 :: ScriptOp
+ Network.Haskoin.Script: OP_NOP5 :: ScriptOp
+ Network.Haskoin.Script: OP_NOP6 :: ScriptOp
+ Network.Haskoin.Script: OP_NOP7 :: ScriptOp
+ Network.Haskoin.Script: OP_NOP8 :: ScriptOp
+ Network.Haskoin.Script: OP_NOP9 :: ScriptOp
+ Network.Haskoin.Script: OP_NOT :: ScriptOp
+ Network.Haskoin.Script: OP_NOTIF :: ScriptOp
+ Network.Haskoin.Script: OP_NUMEQUAL :: ScriptOp
+ Network.Haskoin.Script: OP_NUMEQUALVERIFY :: ScriptOp
+ Network.Haskoin.Script: OP_NUMNOTEQUAL :: ScriptOp
+ Network.Haskoin.Script: OP_OR :: ScriptOp
+ Network.Haskoin.Script: OP_OVER :: ScriptOp
+ Network.Haskoin.Script: OP_PICK :: ScriptOp
+ Network.Haskoin.Script: OP_PUBKEY :: ScriptOp
+ Network.Haskoin.Script: OP_PUBKEYHASH :: ScriptOp
+ Network.Haskoin.Script: OP_PUSHDATA :: !ByteString -> !PushDataType -> ScriptOp
+ Network.Haskoin.Script: OP_RESERVED :: ScriptOp
+ Network.Haskoin.Script: OP_RESERVED1 :: ScriptOp
+ Network.Haskoin.Script: OP_RESERVED2 :: ScriptOp
+ Network.Haskoin.Script: OP_RETURN :: ScriptOp
+ Network.Haskoin.Script: OP_RIGHT :: ScriptOp
+ Network.Haskoin.Script: OP_RIPEMD160 :: ScriptOp
+ Network.Haskoin.Script: OP_ROLL :: ScriptOp
+ Network.Haskoin.Script: OP_ROT :: ScriptOp
+ Network.Haskoin.Script: OP_RSHIFT :: ScriptOp
+ Network.Haskoin.Script: OP_SHA1 :: ScriptOp
+ Network.Haskoin.Script: OP_SHA256 :: ScriptOp
+ Network.Haskoin.Script: OP_SIZE :: ScriptOp
+ Network.Haskoin.Script: OP_SUB :: ScriptOp
+ Network.Haskoin.Script: OP_SUBSTR :: ScriptOp
+ Network.Haskoin.Script: OP_SWAP :: ScriptOp
+ Network.Haskoin.Script: OP_TOALTSTACK :: ScriptOp
+ Network.Haskoin.Script: OP_TUCK :: ScriptOp
+ Network.Haskoin.Script: OP_VER :: ScriptOp
+ Network.Haskoin.Script: OP_VERIF :: ScriptOp
+ Network.Haskoin.Script: OP_VERIFY :: ScriptOp
+ Network.Haskoin.Script: OP_VERNOTIF :: ScriptOp
+ Network.Haskoin.Script: OP_WITHIN :: ScriptOp
+ Network.Haskoin.Script: OP_XOR :: ScriptOp
+ Network.Haskoin.Script: RegularInput :: SimpleInput -> ScriptInput
+ Network.Haskoin.Script: Script :: ![ScriptOp] -> Script
+ Network.Haskoin.Script: data PushDataType
+ Network.Haskoin.Script: data Script
+ Network.Haskoin.Script: data ScriptOp
+ Network.Haskoin.Script: data SimpleInput
+ Network.Haskoin.Script: decodeInputBS :: ByteString -> Either String ScriptInput
+ Network.Haskoin.Script: decodeOutputBS :: ByteString -> Either String ScriptOutput
+ Network.Haskoin.Script: encodeInputBS :: ScriptInput -> ByteString
+ Network.Haskoin.Script: encodeOutputBS :: ScriptOutput -> ByteString
+ Network.Haskoin.Script: evalScript :: Script -> Script -> SigCheck -> Bool
+ Network.Haskoin.Script: getRegularInput :: ScriptInput -> SimpleInput
+ Network.Haskoin.Script: getScriptHashInput :: ScriptInput -> SimpleInput
+ Network.Haskoin.Script: getScriptHashRedeem :: ScriptInput -> RedeemScript
+ Network.Haskoin.Script: isScriptHashInput :: ScriptInput -> Bool
+ Network.Haskoin.Script: opPushData :: ByteString -> ScriptOp
+ Network.Haskoin.Script: scriptOps :: Script -> ![ScriptOp]
+ Network.Haskoin.Script: type SigCheck = [ScriptOp] -> TxSignature -> PubKey -> Bool
+ Network.Haskoin.Script: verifySpend :: Tx -> Int -> Script -> Bool
+ Network.Haskoin.Stratum: StratumAddrBalance :: !Word64 -> !Word64 -> StratumResult
+ Network.Haskoin.Stratum: StratumAddrHistory :: ![StratumTxInfo] -> StratumResult
+ Network.Haskoin.Stratum: StratumAddrStatus :: !Word256 -> StratumResult
+ Network.Haskoin.Stratum: StratumAddrUnspent :: ![StratumCoin] -> StratumResult
+ Network.Haskoin.Stratum: StratumBcastId :: !TxHash -> StratumResult
+ Network.Haskoin.Stratum: StratumBcastTx :: !Tx -> StratumRequest
+ Network.Haskoin.Stratum: StratumCoin :: !OutPoint -> !StratumTxInfo -> !Word64 -> StratumCoin
+ Network.Haskoin.Stratum: StratumNotifAddr :: !Address -> !Word256 -> StratumNotif
+ Network.Haskoin.Stratum: StratumReqBalance :: !Address -> StratumRequest
+ Network.Haskoin.Stratum: StratumReqHistory :: !Address -> StratumRequest
+ Network.Haskoin.Stratum: StratumReqTx :: !TxHash -> StratumRequest
+ Network.Haskoin.Stratum: StratumReqUnspent :: !Address -> StratumRequest
+ Network.Haskoin.Stratum: StratumReqVersion :: !Text -> !Text -> StratumRequest
+ Network.Haskoin.Stratum: StratumSrvVersion :: !String -> StratumResult
+ Network.Haskoin.Stratum: StratumSubAddr :: !Address -> StratumRequest
+ Network.Haskoin.Stratum: StratumTx :: !Tx -> StratumResult
+ Network.Haskoin.Stratum: StratumTxInfo :: !Word -> !TxHash -> StratumTxInfo
+ Network.Haskoin.Stratum: data StratumCoin
+ Network.Haskoin.Stratum: data StratumRequest
+ Network.Haskoin.Stratum: data StratumResult
+ Network.Haskoin.Stratum: data StratumTxInfo
+ Network.Haskoin.Stratum: instance Eq StratumCoin
+ Network.Haskoin.Stratum: instance Eq StratumRequest
+ Network.Haskoin.Stratum: instance Eq StratumResult
+ Network.Haskoin.Stratum: instance Eq StratumTxInfo
+ Network.Haskoin.Stratum: instance FromJSON StratumCoin
+ Network.Haskoin.Stratum: instance FromJSON StratumTxInfo
+ Network.Haskoin.Stratum: instance FromNotif StratumNotif
+ Network.Haskoin.Stratum: instance FromRequest StratumRequest
+ Network.Haskoin.Stratum: instance FromResponse StratumResult
+ Network.Haskoin.Stratum: instance NFData StratumCoin
+ Network.Haskoin.Stratum: instance NFData StratumNotif
+ Network.Haskoin.Stratum: instance NFData StratumRequest
+ Network.Haskoin.Stratum: instance NFData StratumResult
+ Network.Haskoin.Stratum: instance NFData StratumTxInfo
+ Network.Haskoin.Stratum: instance Show StratumCoin
+ Network.Haskoin.Stratum: instance Show StratumRequest
+ Network.Haskoin.Stratum: instance Show StratumResult
+ Network.Haskoin.Stratum: instance Show StratumTxInfo
+ Network.Haskoin.Stratum: instance ToJSON StratumCoin
+ Network.Haskoin.Stratum: instance ToJSON StratumRequest
+ Network.Haskoin.Stratum: instance ToJSON StratumResult
+ Network.Haskoin.Stratum: instance ToJSON StratumTxInfo
+ Network.Haskoin.Stratum: instance ToNotif StratumNotif
+ Network.Haskoin.Stratum: instance ToRequest StratumRequest
+ Network.Haskoin.Stratum: stratumCoinOutPoint :: StratumCoin -> !OutPoint
+ Network.Haskoin.Stratum: stratumCoinTxInfo :: StratumCoin -> !StratumTxInfo
+ Network.Haskoin.Stratum: stratumCoinValue :: StratumCoin -> !Word64
+ Network.Haskoin.Stratum: stratumConfirmed :: StratumResult -> !Word64
+ Network.Haskoin.Stratum: stratumNotifAddr :: StratumNotif -> !Address
+ Network.Haskoin.Stratum: stratumNotifAddrStatus :: StratumNotif -> !Word256
+ Network.Haskoin.Stratum: stratumReqAddr :: StratumRequest -> !Address
+ Network.Haskoin.Stratum: stratumReqClientVer :: StratumRequest -> !Text
+ Network.Haskoin.Stratum: stratumReqProtoVer :: StratumRequest -> !Text
+ Network.Haskoin.Stratum: stratumReqTx :: StratumRequest -> !Tx
+ Network.Haskoin.Stratum: stratumReqTxid :: StratumRequest -> !TxHash
+ Network.Haskoin.Stratum: stratumSrvVersion :: StratumResult -> !String
+ Network.Haskoin.Stratum: stratumTxId :: StratumResult -> !TxHash
+ Network.Haskoin.Stratum: stratumTxInfoHeight :: StratumTxInfo -> !Word
+ Network.Haskoin.Stratum: stratumTxInfoId :: StratumTxInfo -> !TxHash
+ Network.Haskoin.Stratum: stratumUnconfirmed :: StratumResult -> !Word64
+ Network.Haskoin.Transaction: coinScript :: Coin -> !ScriptOutput
+ Network.Haskoin.Transaction: coinValue :: Coin -> !Word64
+ Network.Haskoin.Transaction: detSignInput :: Tx -> Int -> SigInput -> PrvKey -> Either String (Tx, Bool)
+ Network.Haskoin.Transaction: sigDataRedeem :: SigInput -> !(Maybe RedeemScript)
+ Network.Haskoin.Transaction: signInput :: Monad m => Tx -> Int -> SigInput -> PrvKey -> EitherT String (SecretT m) (Tx, Bool)
+ Network.Haskoin.Transaction: verifyStdInput :: Tx -> Int -> ScriptOutput -> Bool
+ Network.Haskoin.Transaction: verifyStdTx :: Tx -> [(ScriptOutput, OutPoint)] -> Bool
- Network.Haskoin.Crypto: PubKeyAddress :: Hash160 -> Address
+ Network.Haskoin.Crypto: PubKeyAddress :: Word160 -> Address
- Network.Haskoin.Crypto: ScriptAddress :: Hash160 -> Address
+ Network.Haskoin.Crypto: ScriptAddress :: Word160 -> Address
- Network.Haskoin.Crypto: buildMerkleRoot :: [Hash256] -> Hash256
+ Network.Haskoin.Crypto: buildMerkleRoot :: [TxHash] -> MerkleRoot
- Network.Haskoin.Crypto: buildPartialMerkle :: [(Hash256, Bool)] -> ([Bool], [Hash256])
+ Network.Haskoin.Crypto: buildPartialMerkle :: [(TxHash, Bool)] -> (FlagBits, PartialMerkleTree)
- Network.Haskoin.Crypto: calcHash :: Int -> Int -> [Hash256] -> Hash256
+ Network.Haskoin.Crypto: calcHash :: Int -> Int -> [TxHash] -> Word256
- Network.Haskoin.Crypto: detSignMsg :: Hash256 -> PrvKey -> Signature
+ Network.Haskoin.Crypto: detSignMsg :: Word256 -> PrvKey -> Signature
- Network.Haskoin.Crypto: doubleHash256 :: ByteString -> Hash256
+ Network.Haskoin.Crypto: doubleHash256 :: ByteString -> Word256
- Network.Haskoin.Crypto: extAddr :: AccPubKey -> KeyIndex -> Maybe String
+ Network.Haskoin.Crypto: extAddr :: AccPubKey -> KeyIndex -> Maybe Address
- Network.Haskoin.Crypto: extAddrs :: AccPubKey -> KeyIndex -> [(String, KeyIndex)]
+ Network.Haskoin.Crypto: extAddrs :: AccPubKey -> KeyIndex -> [(Address, KeyIndex)]
- Network.Haskoin.Crypto: extAddrs' :: AccPubKey -> KeyIndex -> [(String, KeyIndex)]
+ Network.Haskoin.Crypto: extAddrs' :: AccPubKey -> KeyIndex -> [(Address, KeyIndex)]
- Network.Haskoin.Crypto: extMulSigAddr :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> Maybe String
+ Network.Haskoin.Crypto: extMulSigAddr :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> Maybe Address
- Network.Haskoin.Crypto: extMulSigAddrs :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> [(String, KeyIndex)]
+ Network.Haskoin.Crypto: extMulSigAddrs :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> [(Address, KeyIndex)]
- Network.Haskoin.Crypto: extractMatches :: [Bool] -> [Hash256] -> Int -> Either String (Hash256, [Hash256])
+ Network.Haskoin.Crypto: extractMatches :: FlagBits -> PartialMerkleTree -> Int -> Either String (MerkleRoot, [TxHash])
- Network.Haskoin.Crypto: getAddrHash :: Address -> Hash160
+ Network.Haskoin.Crypto: getAddrHash :: Address -> Word160
- Network.Haskoin.Crypto: hash160 :: ByteString -> Hash160
+ Network.Haskoin.Crypto: hash160 :: ByteString -> Word160
- Network.Haskoin.Crypto: hash256 :: ByteString -> Hash256
+ Network.Haskoin.Crypto: hash256 :: ByteString -> Word256
- Network.Haskoin.Crypto: hash512 :: ByteString -> Hash512
+ Network.Haskoin.Crypto: hash512 :: ByteString -> Word512
- Network.Haskoin.Crypto: hmac256 :: ByteString -> ByteString -> Hash256
+ Network.Haskoin.Crypto: hmac256 :: ByteString -> ByteString -> Word256
- Network.Haskoin.Crypto: hmac512 :: ByteString -> ByteString -> Hash512
+ Network.Haskoin.Crypto: hmac512 :: ByteString -> ByteString -> Word512
- Network.Haskoin.Crypto: intAddr :: AccPubKey -> KeyIndex -> Maybe String
+ Network.Haskoin.Crypto: intAddr :: AccPubKey -> KeyIndex -> Maybe Address
- Network.Haskoin.Crypto: intAddrs :: AccPubKey -> KeyIndex -> [(String, KeyIndex)]
+ Network.Haskoin.Crypto: intAddrs :: AccPubKey -> KeyIndex -> [(Address, KeyIndex)]
- Network.Haskoin.Crypto: intAddrs' :: AccPubKey -> KeyIndex -> [(String, KeyIndex)]
+ Network.Haskoin.Crypto: intAddrs' :: AccPubKey -> KeyIndex -> [(Address, KeyIndex)]
- Network.Haskoin.Crypto: intMulSigAddr :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> Maybe String
+ Network.Haskoin.Crypto: intMulSigAddr :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> Maybe Address
- Network.Haskoin.Crypto: intMulSigAddrs :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> [(String, KeyIndex)]
+ Network.Haskoin.Crypto: intMulSigAddrs :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> [(Address, KeyIndex)]
- Network.Haskoin.Crypto: join512 :: (Hash256, Hash256) -> Hash512
+ Network.Haskoin.Crypto: join512 :: (Word256, Word256) -> Word512
- Network.Haskoin.Crypto: mnemonicToSeed :: WordList -> Passphrase -> Mnemonic -> Either String Seed
+ Network.Haskoin.Crypto: mnemonicToSeed :: Passphrase -> Mnemonic -> Either String Seed
- Network.Haskoin.Crypto: signMsg :: Monad m => Hash256 -> PrvKey -> SecretT m Signature
+ Network.Haskoin.Crypto: signMsg :: Monad m => Word256 -> PrvKey -> SecretT m Signature
- Network.Haskoin.Crypto: split512 :: Hash512 -> (Hash256, Hash256)
+ Network.Haskoin.Crypto: split512 :: Word512 -> (Word256, Word256)
- Network.Haskoin.Crypto: toMnemonic :: WordList -> Entropy -> Either String Mnemonic
+ Network.Haskoin.Crypto: toMnemonic :: Entropy -> Either String Mnemonic
- Network.Haskoin.Crypto: type ChainCode = Hash256
+ Network.Haskoin.Crypto: type ChainCode = Word256
- Network.Haskoin.Crypto: type Mnemonic = Text
+ Network.Haskoin.Crypto: type Mnemonic = String
- Network.Haskoin.Crypto: type Passphrase = Text
+ Network.Haskoin.Crypto: type Passphrase = String
- Network.Haskoin.Crypto: type SecretT m a = StateT (SecretState m) m a
+ Network.Haskoin.Crypto: type SecretT m = StateT (SecretState m) m
- Network.Haskoin.Crypto: verifySig :: Hash256 -> Signature -> PubKey -> Bool
+ Network.Haskoin.Crypto: verifySig :: Word256 -> Signature -> PubKey -> Bool
- Network.Haskoin.Crypto: xPrvID :: XPrvKey -> Hash160
+ Network.Haskoin.Crypto: xPrvID :: XPrvKey -> Word160
- Network.Haskoin.Crypto: xPubID :: XPubKey -> Hash160
+ Network.Haskoin.Crypto: xPubID :: XPubKey -> Word160
- Network.Haskoin.Protocol: BlockHeader :: !Word32 -> !Hash256 -> !Hash256 -> !Word32 -> !Word32 -> !Word32 -> BlockHeader
+ Network.Haskoin.Protocol: BlockHeader :: !Word32 -> !BlockHash -> !Word256 -> !Word32 -> !Word32 -> !Word32 -> BlockHeader
- Network.Haskoin.Protocol: BloomFilter :: Seq Word8 -> Bool -> Bool -> Word32 -> Word32 -> BloomFlags -> BloomFilter
+ Network.Haskoin.Protocol: BloomFilter :: !(Seq Word8) -> !Word32 -> !Word32 -> !BloomFlags -> BloomFilter
- Network.Haskoin.Protocol: GetBlocks :: !Word32 -> !BlockLocator -> !Hash256 -> GetBlocks
+ Network.Haskoin.Protocol: GetBlocks :: !Word32 -> !BlockLocator -> !BlockHash -> GetBlocks
- Network.Haskoin.Protocol: GetHeaders :: !Word32 -> !BlockLocator -> !Hash256 -> GetHeaders
+ Network.Haskoin.Protocol: GetHeaders :: !Word32 -> !BlockLocator -> !BlockHash -> GetHeaders
- Network.Haskoin.Protocol: InvVector :: !InvType -> !Hash256 -> InvVector
+ Network.Haskoin.Protocol: InvVector :: !InvType -> !Word256 -> InvVector
- Network.Haskoin.Protocol: MAddr :: Addr -> Message
+ Network.Haskoin.Protocol: MAddr :: !Addr -> Message
- Network.Haskoin.Protocol: MAlert :: Alert -> Message
+ Network.Haskoin.Protocol: MAlert :: !Alert -> Message
- Network.Haskoin.Protocol: MBlock :: Block -> Message
+ Network.Haskoin.Protocol: MBlock :: !Block -> Message
- Network.Haskoin.Protocol: MFilterAdd :: FilterAdd -> Message
+ Network.Haskoin.Protocol: MFilterAdd :: !FilterAdd -> Message
- Network.Haskoin.Protocol: MFilterLoad :: FilterLoad -> Message
+ Network.Haskoin.Protocol: MFilterLoad :: !FilterLoad -> Message
- Network.Haskoin.Protocol: MGetBlocks :: GetBlocks -> Message
+ Network.Haskoin.Protocol: MGetBlocks :: !GetBlocks -> Message
- Network.Haskoin.Protocol: MGetData :: GetData -> Message
+ Network.Haskoin.Protocol: MGetData :: !GetData -> Message
- Network.Haskoin.Protocol: MGetHeaders :: GetHeaders -> Message
+ Network.Haskoin.Protocol: MGetHeaders :: !GetHeaders -> Message
- Network.Haskoin.Protocol: MHeaders :: Headers -> Message
+ Network.Haskoin.Protocol: MHeaders :: !Headers -> Message
- Network.Haskoin.Protocol: MInv :: Inv -> Message
+ Network.Haskoin.Protocol: MInv :: !Inv -> Message
- Network.Haskoin.Protocol: MNotFound :: NotFound -> Message
+ Network.Haskoin.Protocol: MNotFound :: !NotFound -> Message
- Network.Haskoin.Protocol: MPing :: Ping -> Message
+ Network.Haskoin.Protocol: MPing :: !Ping -> Message
- Network.Haskoin.Protocol: MPong :: Pong -> Message
+ Network.Haskoin.Protocol: MPong :: !Pong -> Message
- Network.Haskoin.Protocol: MTx :: Tx -> Message
+ Network.Haskoin.Protocol: MTx :: !Tx -> Message
- Network.Haskoin.Protocol: MVersion :: Version -> Message
+ Network.Haskoin.Protocol: MVersion :: !Version -> Message
- Network.Haskoin.Protocol: MerkleBlock :: !BlockHeader -> !Word32 -> [Hash256] -> [Bool] -> MerkleBlock
+ Network.Haskoin.Protocol: MerkleBlock :: !BlockHeader -> !Word32 -> ![Word256] -> ![Bool] -> MerkleBlock
- Network.Haskoin.Protocol: OutPoint :: !Hash256 -> !Word32 -> OutPoint
+ Network.Haskoin.Protocol: OutPoint :: !TxHash -> !Word32 -> OutPoint
- Network.Haskoin.Protocol: TxIn :: !OutPoint -> !Script -> !Word32 -> TxIn
+ Network.Haskoin.Protocol: TxIn :: !OutPoint -> !ByteString -> !Word32 -> TxIn
- Network.Haskoin.Protocol: TxOut :: !Word64 -> !Script -> TxOut
+ Network.Haskoin.Protocol: TxOut :: !Word64 -> !ByteString -> TxOut
- Network.Haskoin.Protocol: bloomData :: BloomFilter -> Seq Word8
+ Network.Haskoin.Protocol: bloomData :: BloomFilter -> !(Seq Word8)
- Network.Haskoin.Protocol: bloomFlags :: BloomFilter -> BloomFlags
+ Network.Haskoin.Protocol: bloomFlags :: BloomFilter -> !BloomFlags
- Network.Haskoin.Protocol: bloomHashFuncs :: BloomFilter -> Word32
+ Network.Haskoin.Protocol: bloomHashFuncs :: BloomFilter -> !Word32
- Network.Haskoin.Protocol: bloomTweak :: BloomFilter -> Word32
+ Network.Haskoin.Protocol: bloomTweak :: BloomFilter -> !Word32
- Network.Haskoin.Protocol: getBlocksHashStop :: GetBlocks -> !Hash256
+ Network.Haskoin.Protocol: getBlocksHashStop :: GetBlocks -> !BlockHash
- Network.Haskoin.Protocol: getHeadersHashStop :: GetHeaders -> !Hash256
+ Network.Haskoin.Protocol: getHeadersHashStop :: GetHeaders -> !BlockHash
- Network.Haskoin.Protocol: invHash :: InvVector -> !Hash256
+ Network.Haskoin.Protocol: invHash :: InvVector -> !Word256
- Network.Haskoin.Protocol: mFlags :: MerkleBlock -> [Bool]
+ Network.Haskoin.Protocol: mFlags :: MerkleBlock -> ![Bool]
- Network.Haskoin.Protocol: mHashes :: MerkleBlock -> [Hash256]
+ Network.Haskoin.Protocol: mHashes :: MerkleBlock -> ![Word256]
- Network.Haskoin.Protocol: merkleRoot :: BlockHeader -> !Hash256
+ Network.Haskoin.Protocol: merkleRoot :: BlockHeader -> !Word256
- Network.Haskoin.Protocol: outPointHash :: OutPoint -> !Hash256
+ Network.Haskoin.Protocol: outPointHash :: OutPoint -> !TxHash
- Network.Haskoin.Protocol: prevBlock :: BlockHeader -> !Hash256
+ Network.Haskoin.Protocol: prevBlock :: BlockHeader -> !BlockHash
- Network.Haskoin.Protocol: scriptInput :: TxIn -> !Script
+ Network.Haskoin.Protocol: scriptInput :: TxIn -> !ByteString
- Network.Haskoin.Protocol: scriptOutput :: TxOut -> !Script
+ Network.Haskoin.Protocol: scriptOutput :: TxOut -> !ByteString
- Network.Haskoin.Protocol: type BlockLocator = [Hash256]
+ Network.Haskoin.Protocol: type BlockLocator = [BlockHash]
- Network.Haskoin.Script: ScriptHashInput :: ScriptInput -> RedeemScript -> ScriptHashInput
+ Network.Haskoin.Script: ScriptHashInput :: SimpleInput -> RedeemScript -> ScriptInput
- Network.Haskoin.Script: SigAll :: Bool -> SigHash
+ Network.Haskoin.Script: SigAll :: !Bool -> SigHash
- Network.Haskoin.Script: SigNone :: Bool -> SigHash
+ Network.Haskoin.Script: SigNone :: !Bool -> SigHash
- Network.Haskoin.Script: SigSingle :: Bool -> SigHash
+ Network.Haskoin.Script: SigSingle :: !Bool -> SigHash
- Network.Haskoin.Script: SigUnknown :: Bool -> Word8 -> SigHash
+ Network.Haskoin.Script: SigUnknown :: !Bool -> !Word8 -> SigHash
- Network.Haskoin.Script: SpendMulSig :: ![TxSignature] -> !Int -> ScriptInput
+ Network.Haskoin.Script: SpendMulSig :: ![TxSignature] -> SimpleInput
- Network.Haskoin.Script: SpendPK :: !TxSignature -> ScriptInput
+ Network.Haskoin.Script: SpendPK :: !TxSignature -> SimpleInput
- Network.Haskoin.Script: SpendPKHash :: !TxSignature -> !PubKey -> ScriptInput
+ Network.Haskoin.Script: SpendPKHash :: !TxSignature -> !PubKey -> SimpleInput
- Network.Haskoin.Script: TxSignature :: Signature -> SigHash -> TxSignature
+ Network.Haskoin.Script: TxSignature :: !Signature -> !SigHash -> TxSignature
- Network.Haskoin.Script: anyoneCanPay :: SigHash -> Bool
+ Network.Haskoin.Script: anyoneCanPay :: SigHash -> !Bool
- Network.Haskoin.Script: getInputKey :: ScriptInput -> !PubKey
+ Network.Haskoin.Script: getInputKey :: SimpleInput -> !PubKey
- Network.Haskoin.Script: getInputMulSigKeys :: ScriptInput -> ![TxSignature]
+ Network.Haskoin.Script: getInputMulSigKeys :: SimpleInput -> ![TxSignature]
- Network.Haskoin.Script: getInputSig :: ScriptInput -> !TxSignature
+ Network.Haskoin.Script: getInputSig :: SimpleInput -> !TxSignature
- Network.Haskoin.Script: getSigCode :: SigHash -> Word8
+ Network.Haskoin.Script: getSigCode :: SigHash -> !Word8
- Network.Haskoin.Script: sigHashType :: TxSignature -> SigHash
+ Network.Haskoin.Script: sigHashType :: TxSignature -> !SigHash
- Network.Haskoin.Script: txSigHash :: Tx -> Script -> Int -> SigHash -> Hash256
+ Network.Haskoin.Script: txSigHash :: Tx -> Script -> Int -> SigHash -> Word256
- Network.Haskoin.Script: txSignature :: TxSignature -> Signature
+ Network.Haskoin.Script: txSignature :: TxSignature -> !Signature
- Network.Haskoin.Stratum: stratumAddrHist :: StratumResponse -> [TxHeight]
+ Network.Haskoin.Stratum: stratumAddrHist :: StratumResult -> ![StratumTxInfo]
- Network.Haskoin.Stratum: stratumAddrStatus :: StratumResponse -> Hash256
+ Network.Haskoin.Stratum: stratumAddrStatus :: StratumResult -> !Word256
- Network.Haskoin.Stratum: stratumCoins :: StratumResponse -> [Coin]
+ Network.Haskoin.Stratum: stratumCoins :: StratumResult -> ![StratumCoin]
- Network.Haskoin.Stratum: stratumTx :: StratumResponse -> Tx
+ Network.Haskoin.Stratum: stratumTx :: StratumResult -> !Tx
- Network.Haskoin.Transaction: Coin :: TxOut -> OutPoint -> Maybe Script -> Coin
+ Network.Haskoin.Transaction: Coin :: !Word64 -> !ScriptOutput -> !OutPoint -> !(Maybe RedeemScript) -> Coin
- Network.Haskoin.Transaction: SigInput :: Script -> OutPoint -> SigHash -> SigInput
+ Network.Haskoin.Transaction: SigInput :: !ScriptOutput -> !OutPoint -> !SigHash -> !(Maybe RedeemScript) -> SigInput
- Network.Haskoin.Transaction: coinOutPoint :: Coin -> OutPoint
+ Network.Haskoin.Transaction: coinOutPoint :: Coin -> !OutPoint
- Network.Haskoin.Transaction: coinRedeem :: Coin -> Maybe Script
+ Network.Haskoin.Transaction: coinRedeem :: Coin -> !(Maybe RedeemScript)
- Network.Haskoin.Transaction: detSignTx :: Tx -> [SigInput] -> [PrvKey] -> Build Tx
+ Network.Haskoin.Transaction: detSignTx :: Tx -> [SigInput] -> [PrvKey] -> Either String (Tx, Bool)
- Network.Haskoin.Transaction: sigDataOP :: SigInput -> OutPoint
+ Network.Haskoin.Transaction: sigDataOP :: SigInput -> !OutPoint
- Network.Haskoin.Transaction: sigDataOut :: SigInput -> Script
+ Network.Haskoin.Transaction: sigDataOut :: SigInput -> !ScriptOutput
- Network.Haskoin.Transaction: sigDataSH :: SigInput -> SigHash
+ Network.Haskoin.Transaction: sigDataSH :: SigInput -> !SigHash
- Network.Haskoin.Transaction: signTx :: Monad m => Tx -> [SigInput] -> [PrvKey] -> SecretT (BuildT m) Tx
+ Network.Haskoin.Transaction: signTx :: Monad m => Tx -> [SigInput] -> [PrvKey] -> EitherT String (SecretT m) (Tx, Bool)
Files
- Network/Haskoin/Crypto.hs +15/−13
- Network/Haskoin/Crypto/Base58.hs +36/−31
- Network/Haskoin/Crypto/BigWord.hs +104/−39
- Network/Haskoin/Crypto/Bloom.hs +18/−18
- Network/Haskoin/Crypto/ECDSA.hs +15/−12
- Network/Haskoin/Crypto/ExtendedKeys.hs +64/−19
- Network/Haskoin/Crypto/Hash.hs +48/−26
- Network/Haskoin/Crypto/Keys.hs +13/−32
- Network/Haskoin/Crypto/Merkle.hs +24/−17
- Network/Haskoin/Crypto/Mnemonic.hs +52/−57
- Network/Haskoin/Crypto/NormalizedKeys.hs +38/−22
- Network/Haskoin/Crypto/Point.hs +5/−0
- Network/Haskoin/Internals.hs +47/−0
- Network/Haskoin/Protocol.hs +4/−38
- Network/Haskoin/Protocol/Addr.hs +0/−39
- Network/Haskoin/Protocol/Alert.hs +0/−21
- Network/Haskoin/Protocol/Block.hs +0/−38
- Network/Haskoin/Protocol/BlockHeader.hs +0/−63
- Network/Haskoin/Protocol/BloomFilter.hs +0/−101
- Network/Haskoin/Protocol/GetBlocks.hs +0/−53
- Network/Haskoin/Protocol/GetData.hs +0/−34
- Network/Haskoin/Protocol/GetHeaders.hs +0/−46
- Network/Haskoin/Protocol/Headers.hs +0/−36
- Network/Haskoin/Protocol/Inv.hs +0/−29
- Network/Haskoin/Protocol/InvVector.hs +0/−54
- Network/Haskoin/Protocol/MerkleBlock.hs +0/−67
- Network/Haskoin/Protocol/Message.hs +108/−51
- Network/Haskoin/Protocol/MessageHeader.hs +0/−134
- Network/Haskoin/Protocol/NetworkAddress.hs +0/−45
- Network/Haskoin/Protocol/NotFound.hs +0/−30
- Network/Haskoin/Protocol/Ping.hs +0/−37
- Network/Haskoin/Protocol/Script.hs +0/−252
- Network/Haskoin/Protocol/Tx.hs +0/−194
- Network/Haskoin/Protocol/Types.hs +1089/−0
- Network/Haskoin/Protocol/VarInt.hs +0/−46
- Network/Haskoin/Protocol/VarString.hs +0/−29
- Network/Haskoin/Protocol/Version.hs +0/−85
- Network/Haskoin/Script.hs +24/−7
- Network/Haskoin/Script/Evaluator.hs +681/−0
- Network/Haskoin/Script/Parser.hs +131/−84
- Network/Haskoin/Script/SigHash.hs +38/−17
- Network/Haskoin/Script/Types.hs +543/−0
- Network/Haskoin/Stratum.hs +195/−237
- Network/Haskoin/Stratum/Conduit.hs +0/−153
- Network/Haskoin/Stratum/Message.hs +0/−218
- Network/Haskoin/Transaction.hs +4/−1
- Network/Haskoin/Transaction/Builder.hs +229/−216
- Network/Haskoin/Util.hs +13/−35
- Network/Haskoin/Util/BuildMonad.hs +0/−136
- Network/Haskoin/Util/Constants.hs +0/−55
- Network/Haskoin/Util/Constants/Testnet.hs +0/−55
- haskoin.cabal +64/−73
- prodnet/Network/Haskoin/Constants.hs +85/−0
- testnet/Network/Haskoin/Constants.hs +73/−0
- tests/Main.hs +8/−0
- tests/Network/Haskoin/Crypto/Arbitrary.hs +0/−4
- tests/Network/Haskoin/Crypto/Base58/Units.hs +44/−0
- tests/Network/Haskoin/Crypto/ECDSA/Tests.hs +2/−2
- tests/Network/Haskoin/Crypto/Hash/Tests.hs +2/−1
- tests/Network/Haskoin/Crypto/Keys/Tests.hs +5/−4
- tests/Network/Haskoin/Crypto/Merkle/Tests.hs +1/−1
- tests/Network/Haskoin/Crypto/Merkle/Units.hs +3/−4
- tests/Network/Haskoin/Crypto/Mnemonic/Tests.hs +15/−16
- tests/Network/Haskoin/Crypto/Mnemonic/Units.hs +5/−6
- tests/Network/Haskoin/Crypto/Units.hs +3/−3
- tests/Network/Haskoin/Json/Tests.hs +36/−0
- tests/Network/Haskoin/Protocol/Arbitrary.hs +17/−32
- tests/Network/Haskoin/Protocol/Tests.hs +7/−4
- tests/Network/Haskoin/Protocol/Units.hs +2/−2
- tests/Network/Haskoin/Script/Arbitrary.hs +182/−30
- tests/Network/Haskoin/Script/Tests.hs +213/−12
- tests/Network/Haskoin/Script/Units.hs +23/−2
- tests/Network/Haskoin/Stratum/Arbitrary.hs +138/−0
- tests/Network/Haskoin/Stratum/Tests.hs +37/−0
- tests/Network/Haskoin/Stratum/Units.hs +100/−41
- tests/Network/Haskoin/Transaction/Arbitrary.hs +41/−35
- tests/Network/Haskoin/Transaction/Tests.hs +50/−44
- tests/Network/Haskoin/Transaction/Units.hs +7/−5
- tests/Network/Haskoin/Util/Arbitrary.hs +0/−13
- tests/Network/Haskoin/Util/Tests.hs +0/−25
- tests/data/errors.json +6/−7
- tests/data/notifications.json +1/−7
- tests/data/requests.json +7/−12
- tests/data/responses.json +7/−10
- tests/data/script_invalid.json +366/−0
- tests/data/script_valid.json +510/−0
Network/Haskoin/Crypto.hs view
@@ -13,7 +13,6 @@ , isPubKeyU , derivePubKey , pubKeyAddr-, addPubKeys -- **Private Keys , PrvKey(..)@@ -22,7 +21,6 @@ , makePrvKeyU , fromPrvKey , isPrvKeyU-, addPrvKeys , putPrvKey , getPrvKey , getPrvKeyU@@ -32,8 +30,7 @@ -- *ECDSA -- **SecretT Monad -- | The SecretT monad is a monadic wrapper around HMAC DRBG (deterministic- -- random byte generator) using SHA-256. The implementation is provided in - -- 'Network.Haskoin.Crypto.Hash' and the specification is defined in+ -- random byte generator) using SHA-256. The specification is defined in -- <http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf>. The -- SecretT monad is used to generate random private keys and random nonces -- for ECDSA signatures.@@ -59,14 +56,22 @@ , Word128 -- *Hash functions-, Hash512-, Hash256-, Hash160+, TxHash+, BlockHash , CheckSum32+, txHash+, cbHash+, headerHash+, encodeTxHashLE+, decodeTxHashLE+, encodeBlockHashLE+, decodeBlockHashLE , hash512 , hash512BS , hash256 , hash256BS+, hashSha1+, hashSha1BS , hash160 , hash160BS , doubleHash256@@ -94,16 +99,12 @@ , decodeBase58Check -- *Mnemonic keys (BIP-0039)-, WordList , Entropy , Mnemonic , Passphrase , Seed , toMnemonic-, fromMnemonic , mnemonicToSeed-, anyToSeed-, english -- *Extended Keys , ChainCode@@ -205,8 +206,9 @@ , bloomCreate , bloomInsert , bloomContains-, bloomUpdateEmptyFull-, bloomIsValid+, isBloomValid+, isBloomEmpty+, isBloomFull -- *Partial merkle trees , calcTreeHeight
Network/Haskoin/Crypto/Base58.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE OverloadedStrings #-}+ module Network.Haskoin.Crypto.Base58 ( Address(..) , addrToBase58@@ -8,12 +10,15 @@ , decodeBase58Check ) where +import Control.DeepSeq (NFData, rnf) import Control.Monad (guard) import Control.Applicative ((<$>),(<*>)) -import Data.Char (ord)+import Data.Char (ord, chr) import Data.Word (Word8)-import Data.Maybe (fromJust)+import Data.Maybe (fromJust, isJust, listToMaybe)+import Numeric (showIntAtBase, readInt)+import Data.String (fromString) import Data.Aeson ( Value (String) , FromJSON@@ -24,39 +29,39 @@ ) import qualified Data.ByteString as BS-import qualified Data.Map.Strict as M+import qualified Data.ByteString.Char8 as B8 import qualified Data.Text as T -import Network.Haskoin.Crypto.Hash +import Network.Haskoin.Crypto.BigWord+import Network.Haskoin.Crypto.Hash+import Network.Haskoin.Constants import Network.Haskoin.Util -b58String :: String-b58String = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"- b58Data :: BS.ByteString-b58Data = BS.pack $ map (fromIntegral . ord) b58String--b58Data' :: M.Map Word8 Int-b58Data' = M.fromList $ zip (BS.unpack b58Data) [0..57]+b58Data = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" b58 :: Word8 -> Word8 b58 i = BS.index b58Data (fromIntegral i) b58' :: Word8 -> Maybe Word8-b58' w = fromIntegral <$> M.lookup w b58Data'+b58' w = fromIntegral <$> BS.elemIndex w b58Data encodeBase58I :: Integer -> BS.ByteString-encodeBase58I 0 = BS.pack [b58 0]-encodeBase58I i- | i >= 0 = go BS.empty i- | otherwise = error "encodeBase58 is not defined for negative Integers"- where - go acc 0 = acc- go acc n = go (BS.cons (fromIntegral b) acc) q- where - (q,r) = n `quotRem` 58- b = b58 $ fromIntegral r+encodeBase58I i = + fromString $ showIntAtBase (58 :: Integer) f (fromIntegral i) ""+ where+ f = chr . fromIntegral . b58 . fromIntegral +decodeBase58I :: BS.ByteString -> Maybe Integer+decodeBase58I s = case go of + Just (r,[]) -> Just r+ _ -> Nothing+ where+ c = b58' . fromIntegral . ord+ p = isJust . c + f = fromIntegral . fromJust . c+ go = listToMaybe $ readInt 58 p f (B8.unpack s)+ -- | Encode a bytestring to a base 58 representation. encodeBase58 :: BS.ByteString -> BS.ByteString encodeBase58 bs = BS.append l r@@ -74,11 +79,7 @@ (z,b) = BS.span (== (b58 0)) bs prefix = BS.map (fromJust . b58') z -- preserve leading 1's r | BS.null b = Just BS.empty- | otherwise = integerToBS <$> foldl f (Just 0) (BS.unpack b)- f i w = do- n <- fromIntegral <$> b58' w- p <- i- return $ p*58 + n+ | otherwise = integerToBS <$> decodeBase58I b -- | Computes a checksum for the input bytestring and encodes the input and -- the checksum to a base 58 representation.@@ -100,13 +101,17 @@ -- |Data type representing a Bitcoin address data Address -- | Public Key Hash Address- = PubKeyAddress { getAddrHash :: Hash160 }+ = PubKeyAddress { getAddrHash :: Word160 } -- | Script Hash Address- | ScriptAddress { getAddrHash :: Hash160 }- deriving (Eq, Show)+ | ScriptAddress { getAddrHash :: Word160 }+ deriving (Eq, Show, Read) +instance NFData Address where+ rnf (PubKeyAddress h) = rnf h+ rnf (ScriptAddress h) = rnf h+ instance FromJSON Address where- parseJSON = withText "Address not a string: " $ \a -> do+ parseJSON = withText "address" $ \a -> do let s = T.unpack a maybe (fail $ "Not a Bitcoin address: " ++ s) return $ base58ToAddr s
Network/Haskoin/Crypto/BigWord.hs view
@@ -3,9 +3,8 @@ module Network.Haskoin.Crypto.BigWord ( -- Useful type aliases- Hash512-, Hash256-, Hash160+ TxHash+, BlockHash , Word512 , Word256 , Word160@@ -18,16 +17,14 @@ , BigWordMod(..) -- Functions-, toFieldN-, toFieldP-, toMod512-, toMod256-, toMod160-, toMod128 , inverseP , inverseN , quadraticResidue , isIntegerValidKey+, encodeTxHashLE+, decodeTxHashLE+, encodeBlockHashLE+, decodeBlockHashLE ) where import Data.Bits @@ -52,27 +49,38 @@ , putWord8 , putByteString )+import Data.Aeson+ ( Value (String)+ , FromJSON+ , ToJSON+ , parseJSON+ , toJSON+ , withText+ )+import Control.DeepSeq (NFData, rnf) import Control.Monad (unless, guard) import Control.Applicative ((<$>)) import Data.Ratio (numerator, denominator)-import qualified Data.ByteString as BS (head, length)+import qualified Data.ByteString as BS (head, length, reverse)+import qualified Data.Text as T (pack, unpack) import Network.Haskoin.Crypto.Curve import Network.Haskoin.Crypto.NumberTheory import Network.Haskoin.Util +-- | Type representing a transaction hash.+type TxHash = BigWord Mod256Tx+-- | Type representing a block hash.+type BlockHash = BigWord Mod256Block -- | Data type representing a 512 bit unsigned integer. -- It is implemented as an Integer modulo 2^512. type Word512 = BigWord Mod512-type Hash512 = Word512 -- | Data type representing a 256 bit unsigned integer. -- It is implemented as an Integer modulo 2^256. type Word256 = BigWord Mod256-type Hash256 = Word256 -- | Data type representing a 160 bit unsigned integer. -- It is implemented as an Integer modulo 2^160. type Word160 = BigWord Mod160-type Hash160 = Word160 -- | Data type representing a 128 bit unsigned integer. -- It is implemented as an Integer modulo 2^128. type Word128 = BigWord Mod128@@ -83,6 +91,8 @@ data Mod512 data Mod256 +data Mod256Tx+data Mod256Block data Mod160 data Mod128 data ModP@@ -91,23 +101,8 @@ newtype BigWord n = BigWord { getBigWordInteger :: Integer } deriving (Eq, Ord, Read, Show) -toFieldN :: BigWord n -> FieldN-toFieldN (BigWord i) = fromInteger i--toFieldP :: BigWord n -> FieldP-toFieldP (BigWord i) = fromInteger i--toMod512 :: BigWord n -> Word512-toMod512 (BigWord i) = fromInteger i--toMod256 :: BigWord n -> Word256-toMod256 (BigWord i) = fromInteger i--toMod160 :: BigWord n -> Word160-toMod160 (BigWord i) = fromInteger i--toMod128 :: BigWord n -> Word128-toMod128 (BigWord i) = fromInteger i+instance NFData (BigWord n) where+ rnf (BigWord n) = rnf n inverseP :: FieldP -> FieldP inverseP (BigWord i) = fromInteger $ mulInverse i curveP@@ -127,6 +122,14 @@ rFromInteger i = BigWord $ i `mod` 2 ^ (256 :: Int) rBitSize _ = 256 +instance BigWordMod Mod256Tx where+ rFromInteger i = BigWord $ i `mod` 2 ^ (256 :: Int)+ rBitSize _ = 256++instance BigWordMod Mod256Block where+ rFromInteger i = BigWord $ i `mod` 2 ^ (256 :: Int)+ rBitSize _ = 256+ instance BigWordMod Mod160 where rFromInteger i = BigWord $ i `mod` 2 ^ (160 :: Int) rBitSize _ = 160@@ -216,13 +219,13 @@ instance Binary (BigWord Mod512) where get = do- a <- fromIntegral <$> (get :: Get Hash256)- b <- fromIntegral <$> (get :: Get Hash256)+ a <- fromIntegral <$> (get :: Get Word256)+ b <- fromIntegral <$> (get :: Get Word256) return $ (a `shiftL` 256) + b put (BigWord i) = do- put $ (fromIntegral (i `shiftR` 256) :: Hash256)- put $ (fromIntegral i :: Hash256)+ put $ (fromIntegral (i `shiftR` 256) :: Word256)+ put $ (fromIntegral i :: Word256) instance Binary (BigWord Mod256) where get = do@@ -238,6 +241,34 @@ putWord64be $ fromIntegral (i `shiftR` 64) putWord64be $ fromIntegral i +instance Binary (BigWord Mod256Tx) where+ get = do+ a <- fromIntegral <$> getWord64be+ b <- fromIntegral <$> getWord64be+ c <- fromIntegral <$> getWord64be+ d <- fromIntegral <$> getWord64be+ return $ (a `shiftL` 192) + (b `shiftL` 128) + (c `shiftL` 64) + d++ put (BigWord i) = do+ putWord64be $ fromIntegral (i `shiftR` 192)+ putWord64be $ fromIntegral (i `shiftR` 128)+ putWord64be $ fromIntegral (i `shiftR` 64)+ putWord64be $ fromIntegral i++instance Binary (BigWord Mod256Block) where+ get = do+ a <- fromIntegral <$> getWord64be+ b <- fromIntegral <$> getWord64be+ c <- fromIntegral <$> getWord64be+ d <- fromIntegral <$> getWord64be+ return $ (a `shiftL` 192) + (b `shiftL` 128) + (c `shiftL` 64) + d++ put (BigWord i) = do+ putWord64be $ fromIntegral (i `shiftR` 192)+ putWord64be $ fromIntegral (i `shiftR` 128)+ putWord64be $ fromIntegral (i `shiftR` 64)+ putWord64be $ fromIntegral i+ instance Binary (BigWord Mod160) where get = do a <- fromIntegral <$> getWord32be@@ -268,8 +299,6 @@ unless (t == 0x02) (fail $ "Bad DER identifier byte " ++ (show t) ++ ". Expecting 0x02" ) l <- getWord8- unless (l <= 33) (fail $- "Bad DER length " ++ (show l) ++ ". Expecting length <= 33" ) i <- bsToInteger <$> getByteString (fromIntegral l) unless (isIntegerValidKey i) $ fail $ "Invalid fieldN element: " ++ (show i)@@ -292,14 +321,30 @@ -- Section 2.3.6 http://www.secg.org/download/aid-780/sec1-v2.pdf get = do- (BigWord i) <- get :: Get Hash256+ (BigWord i) <- get :: Get Word256 unless (i < curveP) (fail $ "Get: Integer not in FieldP: " ++ (show i)) return $ fromInteger i -- Section 2.3.7 http://www.secg.org/download/aid-780/sec1-v2.pdf- put r = put $ toMod256 r- + put r = put (fromIntegral r :: Word256)+ +instance ToJSON (BigWord Mod256Tx) where+ toJSON = String . T.pack . encodeTxHashLE +instance FromJSON (BigWord Mod256Tx) where+ parseJSON = withText "TxHash not a string: " $ \a -> do+ let s = T.unpack a+ maybe (fail $ "Not a TxHash: " ++ s) return $ decodeTxHashLE s++instance ToJSON (BigWord Mod256) where+ toJSON = String . T.pack . bsToHex . encode'++instance FromJSON (BigWord Mod256) where+ parseJSON = withText "Word256 not a string: " $ \a -> do+ let s = T.unpack a+ maybe (fail $ "Not a Word256: " ++ s) return $ + hexToBS s >>= decodeToMaybe+ -- curveP = 3 (mod 4), thus Lagrange solutions apply -- http://en.wikipedia.org/wiki/Quadratic_residue quadraticResidue :: FieldP -> [FieldP]@@ -310,4 +355,24 @@ isIntegerValidKey :: Integer -> Bool isIntegerValidKey i = i > 0 && i < curveN++-- | Encodes a 'TxHash' as little endian in HEX format. This is mostly used for+-- displaying transaction ids. Internally, these ids are handled as big endian+-- but are transformed to little endian when displaying them.+encodeTxHashLE :: TxHash -> String+encodeTxHashLE = bsToHex . BS.reverse . encode' ++-- | Decodes a little endian 'TxHash' in HEX format. +decodeTxHashLE :: String -> Maybe TxHash+decodeTxHashLE = (decodeToMaybe . BS.reverse =<<) . hexToBS++-- | Encodes a 'BlockHash' as little endian in HEX format. This is mostly used+-- for displaying Block hash ids. Internally, these ids are handled as big+-- endian but are transformed to little endian when displaying them.+encodeBlockHashLE :: BlockHash -> String+encodeBlockHashLE = bsToHex . BS.reverse . encode' ++-- | Decodes a little endian 'BlockHash' in HEX format. +decodeBlockHashLE :: String -> Maybe BlockHash+decodeBlockHashLE = (decodeToMaybe . BS.reverse =<<) . hexToBS
Network/Haskoin/Crypto/Bloom.hs view
@@ -4,8 +4,9 @@ , bloomCreate , bloomInsert , bloomContains-, bloomUpdateEmptyFull-, bloomIsValid+, isBloomValid+, isBloomEmpty+, isBloomFull ) where import Data.Word@@ -44,7 +45,7 @@ -> BloomFlags -- ^ Bloom filter flags -> BloomFilter -- ^ Bloom filter bloomCreate numElem fpRate tweak flags =- BloomFilter (S.replicate bloomSize 0) False False numHashF tweak flags+ BloomFilter (S.replicate bloomSize 0) numHashF tweak flags where bloomSize = truncate $ (min a b) / 8 a = -1 / ln2Squared * (fromIntegral numElem) * log fpRate@@ -64,8 +65,8 @@ -> BS.ByteString -- ^ New data to insert -> BloomFilter -- ^ Bloom filter containing the new data bloomInsert bfilter bs - | bloomFull bfilter = bfilter- | otherwise = bfilter { bloomData = newData, bloomEmpty = False }+ | isBloomFull bfilter = bfilter+ | otherwise = bfilter { bloomData = newData } where idxs = map (\i -> bloomHash bfilter i bs) [0..bloomHashFuncs bfilter - 1] upd s i = S.adjust (.|. bitMask !! (7 .&. i)) (i `shiftR` 3) s@@ -79,9 +80,9 @@ -> Bool -- ^ Returns True if the data matches the filter bloomContains bfilter bs- | bloomFull bfilter = True- | bloomEmpty bfilter = False- | otherwise = and $ map isSet idxs+ | isBloomFull bfilter = True+ | isBloomEmpty bfilter = False+ | otherwise = and $ map isSet idxs where s = bloomData bfilter idxs = map (\i -> bloomHash bfilter i bs) [0..bloomHashFuncs bfilter - 1]@@ -90,19 +91,18 @@ -- TODO: Write bloomRelevantUpdate -- bloomRelevantUpdate :: BloomFilter -> Tx -> Hash256 -> Maybe BloomFilter --- | Sets the bloom filter empty flag to True if the filter is empty, and--- the bloom filter full flag to True if the filter is full.-bloomUpdateEmptyFull :: BloomFilter -- ^ Bloom filter to update- -> BloomFilter -- ^ Filter with updated flags-bloomUpdateEmptyFull bfilter = - bfilter { bloomEmpty = all (== 0x00) l, bloomFull = all (== 0xff) l }- where- l = F.toList $ bloomData bfilter+-- | Returns True if the filter is empty (all bytes set to 0x00)+isBloomEmpty :: BloomFilter -> Bool+isBloomEmpty bfilter = all (== 0x00) $ F.toList $ bloomData bfilter +-- | Returns True if the filter is full (all bytes set to 0xff)+isBloomFull :: BloomFilter -> Bool+isBloomFull bfilter = all (== 0xff) $ F.toList $ bloomData bfilter+ -- | Tests if a given bloom filter is valid.-bloomIsValid :: BloomFilter -- ^ Bloom filter to test+isBloomValid :: BloomFilter -- ^ Bloom filter to test -> Bool -- ^ True if the given filter is valid-bloomIsValid bfilter =+isBloomValid bfilter = (S.length $ bloomData bfilter) <= maxBloomSize && (bloomHashFuncs bfilter) <= maxHashFuncs
Network/Haskoin/Crypto/ECDSA.hs view
@@ -15,6 +15,7 @@ import System.IO +import Control.DeepSeq (NFData, rnf) import Control.Monad (liftM, guard, unless) import Control.Monad.Trans (MonadTrans, lift) import Control.Applicative (Applicative, (<*>), (<$>))@@ -37,6 +38,7 @@ ) import Network.Haskoin.Util +import Network.Haskoin.Constants import Network.Haskoin.Crypto.Hash import Network.Haskoin.Crypto.Keys import Network.Haskoin.Crypto.Point @@ -48,7 +50,7 @@ -- | StateT monad stack tracking the internal state of HMAC DRBG -- pseudo random number generator using SHA-256. The 'SecretT' monad is -- run with the 'withSource' function by providing it a source of entropy.-type SecretT m a = S.StateT (SecretState m) m a+type SecretT m = S.StateT (SecretState m) m -- | Run a 'SecretT' monad by providing it a source of entropy. You can -- use 'devURandom', 'devRandom' or provide your own entropy source function.@@ -109,12 +111,15 @@ Signature { sigR :: !FieldN , sigS :: !FieldN }- deriving (Show, Eq)+ deriving (Read, Show, Eq) +instance NFData Signature where+ rnf (Signature r s) = rnf r `seq` rnf s+ -- Section 4.1.3 http://www.secg.org/download/aid-780/sec1-v2.pdf -- | Safely sign a message inside the 'SecretT' monad. The 'SecretT' monad will -- generate a new nonce for each signature.-signMsg :: Monad m => Hash256 -> PrvKey -> SecretT m Signature+signMsg :: Monad m => Word256 -> PrvKey -> SecretT m Signature signMsg _ (PrvKey 0) = error "signMsg: Invalid private key 0" signMsg _ (PrvKeyU 0) = error "signMsg: Invalid private key 0" signMsg h d = do@@ -127,7 +132,7 @@ -- | Sign a message using ECDSA deterministic signatures as defined by -- RFC 6979 <http://tools.ietf.org/html/rfc6979>-detSignMsg :: Hash256 -> PrvKey -> Signature+detSignMsg :: Word256 -> PrvKey -> Signature detSignMsg _ (PrvKey 0) = error "detSignMsg: Invalid private key 0" detSignMsg _ (PrvKeyU 0) = error "detSignMsg: Invalid private key 0" detSignMsg h d = go $ hmacDRBGNew (runPut' $ putPrvKey d) (encode' h) BS.empty@@ -146,16 +151,16 @@ -- Re-using the same nonce twice will expose the private keys -- Use signMsg within the SecretT monad or detSignMsg instead -- Section 4.1.3 http://www.secg.org/download/aid-780/sec1-v2.pdf-unsafeSignMsg :: Hash256 -> FieldN -> (FieldN, Point) -> Maybe Signature+unsafeSignMsg :: Word256 -> FieldN -> (FieldN, Point) -> Maybe Signature unsafeSignMsg _ 0 _ = Nothing unsafeSignMsg h d (k,p) = do -- 4.1.3.1 (4.1.3.2 not required) (x,_) <- getAffine p -- 4.1.3.3- let r = toFieldN x+ let r = (fromIntegral x :: FieldN) guard (r /= 0) -- 4.1.3.4 / 4.1.3.5- let e = toFieldN h+ let e = (fromIntegral h :: FieldN) -- 4.1.3.6 let s' = (e + r*d)/k -- Canonicalize signatures: s <= order/2@@ -167,17 +172,17 @@ -- Section 4.1.4 http://www.secg.org/download/aid-780/sec1-v2.pdf -- | Verify an ECDSA signature-verifySig :: Hash256 -> Signature -> PubKey -> Bool+verifySig :: Word256 -> Signature -> PubKey -> Bool -- 4.1.4.1 (r and s can not be zero) verifySig _ (Signature 0 _) _ = False verifySig _ (Signature _ 0) _ = False verifySig h (Signature r s) q = case getAffine p of Nothing -> False -- 4.1.4.7 / 4.1.4.8- (Just (x,_)) -> (toFieldN x) == r+ (Just (x,_)) -> (fromIntegral x :: FieldN) == r where -- 4.1.4.2 / 4.1.4.3- e = toFieldN h+ e = (fromIntegral h :: FieldN) -- 4.1.4.4 s' = inverseN s u1 = e*s'@@ -198,8 +203,6 @@ "Bad DER identifier byte " ++ (show t) ++ ". Expecting 0x30") l <- getWord8 -- Length = (33 + 1 identifier byte + 1 length byte) * 2- unless (l <= 70) (fail $- "Bad DER length " ++ (show t) ++ ". Expecting length <= 70") isolate (fromIntegral l) $ do Signature <$> get <*> get
Network/Haskoin/Crypto/ExtendedKeys.hs view
@@ -28,38 +28,35 @@ , xPrvWIF , cycleIndex , cycleIndex'+, addPubKeys+, addPrvKeys ) where -import Control.Monad - ( guard- , unless- , when- , liftM2- )+import Control.DeepSeq (NFData, rnf)+import Control.Monad (mzero, guard, unless, when, liftM2)++import Data.Aeson (Value(String), FromJSON, ToJSON, parseJSON, toJSON, withText) import Data.Binary (Binary, get, put) import Data.Binary.Get (Get, getWord8, getWord32be) import Data.Binary.Put (Put, runPut, putWord8, putWord32be) import Data.Word (Word8, Word32)-import Data.Bits - ( shiftR- , setBit- , testBit- , clearBit- )+import Data.Bits (shiftR, setBit, testBit, clearBit) import Data.Maybe (mapMaybe)-import qualified Data.ByteString as BS - ( ByteString- , append- )+import qualified Data.Text as T (pack, unpack)+import qualified Data.ByteString as BS (ByteString, append) import Network.Haskoin.Util+import Network.Haskoin.Constants import Network.Haskoin.Crypto.Keys import Network.Haskoin.Crypto.Hash import Network.Haskoin.Crypto.Base58+import Network.Haskoin.Crypto.BigWord+import Network.Haskoin.Crypto.Curve+import Network.Haskoin.Crypto.Point {- See BIP32 for details: https://en.bitcoin.it/wiki/BIP_0032 -} -type ChainCode = Hash256+type ChainCode = Word256 -- | Data type representing an extended BIP32 private key. An extended key -- is a node in a tree of key derivations. It has a depth in the tree, a @@ -72,6 +69,17 @@ , xPrvKey :: !PrvKey -- ^ The private key of this extended key node. } deriving (Eq, Show, Read) +instance NFData XPrvKey where+ rnf (XPrvKey d p i c k) =+ rnf d `seq` rnf p `seq` rnf i `seq` rnf c `seq` rnf k++instance ToJSON XPrvKey where+ toJSON = String . T.pack . xPrvExport++instance FromJSON XPrvKey where+ parseJSON = withText "xprvkey" $ \t -> + maybe mzero return $ xPrvImport (T.unpack t)+ -- | Data type representing an extended BIP32 public key. data XPubKey = XPubKey { xPubDepth :: !Word8 -- ^ Depth in the tree of key derivations.@@ -81,6 +89,17 @@ , xPubKey :: !PubKey -- ^ The public key of this extended key node. } deriving (Eq, Show, Read) +instance NFData XPubKey where+ rnf (XPubKey d p i c k) =+ rnf d `seq` rnf p `seq` rnf i `seq` rnf c `seq` rnf k++instance ToJSON XPubKey where+ toJSON = String . T.pack . xPubExport++instance FromJSON XPubKey where+ parseJSON = withText "xpubkey" $ \t -> + maybe mzero return $ xPubImport (T.unpack t)+ -- | Build a BIP32 compatible extended private key from a bytestring. This will -- produce a root node (depth=0 and parent=0). makeXPrvKey :: BS.ByteString -> Maybe XPrvKey@@ -141,6 +160,32 @@ msg = BS.append (bsPadPrvKey $ xPrvKey xkey) (encode' i) (a,c) = split512 $ hmac512 (encode' $ xPrvChain xkey) msg +-- Add two private keys together. One of the keys is defined by a Word256.+-- The functions fails on uncompressed private keys and return Nothing if the+-- Word256 is smaller than the order of the curve N.+addPrvKeys :: PrvKey -> Word256 -> Maybe PrvKey+addPrvKeys key i+ | isPrvKeyU key = error "Add: HDW only supports compressed formats"+ | toInteger i < curveN =+ let r = (prvKeyFieldN key) + (fromIntegral i :: FieldN) + in makePrvKey $ toInteger r+ | otherwise = Nothing++-- Add a public key to a private key defined by its Word256 value. This will+-- transform the private key into a public key and add the respective public+-- key points together. This function fails for uncompressed keys and returns+-- Nothing if the private key value is >= than the order of the curve N.+addPubKeys :: PubKey -> Word256 -> Maybe PubKey+addPubKeys pub i+ | isPubKeyU pub = error "Add: HDW only supports compressed formats"+ | toInteger i < curveN =+ let pt1 = mulPoint (fromIntegral i :: FieldN) curveG+ pt2 = addPoint (pubKeyPoint pub) pt1+ in if isInfPoint pt2 then Nothing+ else Just $ PubKey pt2+ | otherwise = Nothing++ -- | Cyclic list of all private non-prime child key derivations of a parent key -- starting from an offset index. prvSubKeys :: XPrvKey -> Word32 -> [(XPrvKey,Word32)]@@ -217,11 +262,11 @@ xPubChild k = clearBit (xPubIndex k) 31 -- | Computes the key identifier of an extended private key.-xPrvID :: XPrvKey -> Hash160+xPrvID :: XPrvKey -> Word160 xPrvID = xPubID . deriveXPubKey -- | Computes the key identifier of an extended public key.-xPubID :: XPubKey -> Hash160+xPubID :: XPubKey -> Word160 xPubID = hash160 . hash256BS . encode' . xPubKey -- | Computes the key fingerprint of an extended private key.
Network/Haskoin/Crypto/Hash.hs view
@@ -1,14 +1,13 @@ -- | Hashing functions and HMAC DRBG definition module Network.Haskoin.Crypto.Hash-( Hash512-, Hash256-, Hash160-, CheckSum32+( CheckSum32 , hash512 , hash256+, hashSha1 , hash160 , hash512BS , hash256BS+, hashSha1BS , hash160BS , doubleHash256 , doubleHash256BS@@ -27,15 +26,18 @@ , join512 , decodeCompact , encodeCompact+, txHash+, cbHash+, headerHash ) where -import Control.Applicative ((<$>)) import Control.Monad (replicateM) import Crypto.Hash - ( Digest + ( Digest , SHA512 , SHA256+ , SHA1 , RIPEMD160 , hash )@@ -43,7 +45,7 @@ import Data.Word (Word16, Word32) import Data.Byteable (toBytes)-import Data.Binary (Binary, get, put)+import Data.Binary (Binary, get) import Data.Binary.Get (getWord32le) import Data.Bits ( shiftL@@ -68,13 +70,9 @@ import Network.Haskoin.Util import Network.Haskoin.Crypto.BigWord ---- | Data type representing a 32 bit checksum-newtype CheckSum32 = CheckSum32 Word32 deriving (Show, Eq, Read)+import Network.Haskoin.Protocol.Types -instance Binary CheckSum32 where- get = CheckSum32 <$> get- put (CheckSum32 w) = put w+type CheckSum32 = Word32 run512 :: BS.ByteString -> BS.ByteString run512 = (toBytes :: Digest SHA512 -> BS.ByteString) . hash@@ -85,8 +83,11 @@ run160 :: BS.ByteString -> BS.ByteString run160 = (toBytes :: Digest RIPEMD160 -> BS.ByteString) . hash +runSha1 :: BS.ByteString -> BS.ByteString+runSha1 = (toBytes :: Digest SHA1 -> BS.ByteString) . hash+ -- | Computes SHA-512.-hash512 :: BS.ByteString -> Hash512+hash512 :: BS.ByteString -> Word512 hash512 bs = runGet' get (run512 bs) -- | Computes SHA-512 and returns the result as a bytestring.@@ -94,15 +95,23 @@ hash512BS bs = run512 bs -- | Computes SHA-256.-hash256 :: BS.ByteString -> Hash256+hash256 :: BS.ByteString -> Word256 hash256 bs = runGet' get (run256 bs) -- | Computes SHA-256 and returns the result as a bytestring. hash256BS :: BS.ByteString -> BS.ByteString hash256BS bs = run256 bs +-- | Computes SHA-160.+hashSha1 :: BS.ByteString -> Word160+hashSha1 bs = runGet' get (runSha1 bs)++-- | Computes SHA-160 and returns the result as a bytestring.+hashSha1BS :: BS.ByteString -> BS.ByteString+hashSha1BS bs = runSha1 bs+ -- | Computes RIPEMD-160.-hash160 :: BS.ByteString -> Hash160+hash160 :: BS.ByteString -> Word160 hash160 bs = runGet' get (run160 bs) -- | Computes RIPEMD-160 and returns the result as a bytestring.@@ -110,7 +119,7 @@ hash160BS bs = run160 bs -- | Computes two rounds of SHA-256.-doubleHash256 :: BS.ByteString -> Hash256+doubleHash256 :: BS.ByteString -> Word256 doubleHash256 bs = runGet' get (run256 $ run256 bs) -- | Computes two rounds of SHA-256 and returns the result as a bytestring.@@ -121,12 +130,12 @@ -- | Computes a 32 bit checksum. chksum32 :: BS.ByteString -> CheckSum32-chksum32 bs = CheckSum32 $ fromIntegral $ (doubleHash256 bs) `shiftR` 224+chksum32 bs = fromIntegral $ (doubleHash256 bs) `shiftR` 224 {- HMAC -} -- | Computes HMAC over SHA-512.-hmac512 :: BS.ByteString -> BS.ByteString -> Hash512+hmac512 :: BS.ByteString -> BS.ByteString -> Word512 hmac512 key = decode' . (hmac512BS key) -- | Computes HMAC over SHA-512 and return the result as a bytestring.@@ -134,20 +143,21 @@ hmac512BS key msg = hmac hash512BS 128 key msg -- | Computes HMAC over SHA-256.-hmac256 :: BS.ByteString -> BS.ByteString -> Hash256+hmac256 :: BS.ByteString -> BS.ByteString -> Word256 hmac256 key = decode' . (hmac256BS key) -- | Computes HMAC over SHA-256 and return the result as a bytestring. hmac256BS :: BS.ByteString -> BS.ByteString -> BS.ByteString hmac256BS key msg = hmac hash256BS 64 key msg --- | Split a 'Hash512' into a pair of 'Hash256'.-split512 :: Hash512 -> (Hash256, Hash256)+-- | Split a 'Word512' into a pair of 'Word256'.+split512 :: Word512 -> (Word256, Word256) split512 i = (fromIntegral $ i `shiftR` 256, fromIntegral i) --- | Join a pair of 'Hash256' into a 'Hash512'.-join512 :: (Hash256, Hash256) -> Hash512-join512 (a,b) = ((toMod512 a) `shiftL` 256) + (toMod512 b)+-- | Join a pair of 'Word256' into a 'Word512'.+join512 :: (Word256, Word256) -> Word512+join512 (a,b) = + ((fromIntegral a :: Word512) `shiftL` 256) + (fromIntegral b :: Word512) -- | Decode the compact number used in the difficulty target of a block into an -- Integer. @@ -186,6 +196,18 @@ | otherwise = (s1, c1) c3 = fromIntegral $ c2 .|. ((toInteger s2) `shiftL` 24) +-- | Computes the hash of a transaction.+txHash :: Tx -> TxHash+txHash = fromIntegral . doubleHash256 . encode' ++-- | Computes the hash of a coinbase transaction.+cbHash :: CoinbaseTx -> TxHash+cbHash = fromIntegral . doubleHash256 . encode' ++-- | Compute the hash of a block header+headerHash :: BlockHeader -> BlockHash+headerHash = fromIntegral . doubleHash256 . encode'+ {- 10.1.2 HMAC_DRBG with HMAC-SHA256 http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf Constants are based on recommentations in Appendix D section 2 (D.2)@@ -259,7 +281,7 @@ {- MurmurHash3 -} -- | MurmurHash3 (x86_32). For more details, see--- http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp+-- <http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp> -- This code is used in the bloom filters of SPV nodes. murmurHash3 :: Word32 -> BS.ByteString -> Word32 murmurHash3 nHashSeed bs = h8
Network/Haskoin/Crypto/Keys.hs view
@@ -4,14 +4,12 @@ , isPubKeyU , derivePubKey , pubKeyAddr-, addPubKeys , PrvKey(..) , isValidPrvKey , makePrvKey , makePrvKeyU , fromPrvKey , isPrvKeyU-, addPrvKeys , putPrvKey , getPrvKey , getPrvKeyU@@ -24,6 +22,7 @@ import Data.Binary.Get (Get, getWord8) import Data.Binary.Put (Put, putWord8) +import Control.DeepSeq (NFData, rnf) import Control.Monad (when, unless, guard) import Control.Applicative ((<$>),(<*>)) import Data.Maybe (isJust, fromJust)@@ -39,6 +38,7 @@ import Network.Haskoin.Crypto.Point import Network.Haskoin.Crypto.Base58 import Network.Haskoin.Crypto.Hash +import Network.Haskoin.Constants import Network.Haskoin.Util -- | G parameter of the EC curve expressed as a Point@@ -57,6 +57,10 @@ | PubKeyU { pubKeyPoint :: !Point } deriving (Read, Show) +instance NFData PubKey where+ rnf (PubKey p) = rnf p+ rnf (PubKeyU p) = rnf p+ instance Eq PubKey where -- Compression does not matter for InfPoint (PubKey InfPoint) == (PubKeyU InfPoint) = True@@ -70,21 +74,6 @@ isValidPubKey :: PubKey -> Bool isValidPubKey = validatePoint . pubKeyPoint --- | Add a public key to a private key defined by its Hash256 value. This will--- transform the private key into a public key and add the respective public--- key points together. This is provided as a helper for BIP32 wallet--- implementations. This function fails for uncompressed keys and returns--- Nothing if the private key value is >= than the order of the curve N.-addPubKeys :: PubKey -> Hash256 -> Maybe PubKey-addPubKeys pub i- | isPubKeyU pub = error "Add: HDW only supports compressed formats"- | toInteger i < curveN =- let pt1 = mulPoint (toFieldN i) curveG- pt2 = addPoint (pubKeyPoint pub) pt1- in if isInfPoint pt2 then Nothing- else Just $ PubKey pt2- | otherwise = Nothing- -- | Returns True if the public key is uncompressed isPubKeyU :: PubKey -> Bool isPubKeyU (PubKey _) = False@@ -161,6 +150,10 @@ | PrvKeyU { prvKeyFieldN :: !FieldN } deriving (Eq, Show, Read) +instance NFData PrvKey where+ rnf (PrvKey p) = rnf p+ rnf (PrvKeyU p) = rnf p+ -- | Returns True if the private key is valid. This will check if the integer -- value representing the private key is greater than 0 and smaller than the -- curve order N.@@ -187,18 +180,6 @@ fromPrvKey :: PrvKey -> Integer fromPrvKey = fromIntegral . prvKeyFieldN --- | Add two private keys together. One of the keys is defined by a Hash256.--- The functions fails on uncompressed private keys and return Nothing if the--- Hash256 is smaller than the order of the curve N. This is provided--- as a helper for implementing BIP32 wallets.-addPrvKeys :: PrvKey -> Hash256 -> Maybe PrvKey-addPrvKeys key i- | isPrvKeyU key = error "Add: HDW only supports compressed formats"- | toInteger i < curveN =- let r = (prvKeyFieldN key) + (toFieldN i) - in makePrvKey $ toInteger r- | otherwise = Nothing- -- | Returns True of the private key is uncompressed isPrvKeyU :: PrvKey -> Bool isPrvKeyU (PrvKey _) = False@@ -209,13 +190,13 @@ -- format for private keys is required putPrvKey :: PrvKey -> Put putPrvKey k | prvKeyFieldN k == 0 = error "Put: 0 is an invalid private key"- | otherwise = put $ toMod256 $ prvKeyFieldN k+ | otherwise = put $ (fromIntegral (prvKeyFieldN k) :: Word256) -- | Deserializes a compressed private key from the Data.Binary.Get monad as a -- 32 byte big endian ByteString. getPrvKey :: Get PrvKey getPrvKey = do- i <- get :: Get Hash256+ i <- get :: Get Word256 let res = makePrvKey $ fromIntegral i unless (isJust res) $ fail "Get: PrivateKey is invalid" return $ fromJust res@@ -224,7 +205,7 @@ -- a 32 byte big endian ByteString getPrvKeyU :: Get PrvKey getPrvKeyU = do- i <- get :: Get Hash256+ i <- get :: Get Word256 let res = makePrvKeyU $ fromIntegral i unless (isJust res) $ fail "Get: PrivateKey is invalid" return $ fromJust res
Network/Haskoin/Crypto/Merkle.hs view
@@ -12,8 +12,14 @@ import qualified Data.ByteString as BS import Network.Haskoin.Crypto.Hash+import Network.Haskoin.Crypto.BigWord import Network.Haskoin.Util+import Network.Haskoin.Constants +type MerkleRoot = Word256+type FlagBits = [Bool]+type PartialMerkleTree = [Word256]+ -- | Computes the height of a merkle tree. calcTreeHeight :: Int -- ^ Number of transactions (leaf nodes). -> Int -- ^ Height of the merkle tree.@@ -27,21 +33,21 @@ calcTreeWidth ntx h = (ntx + (1 `shiftL` h) - 1) `shiftR` h -- | Computes the root of a merkle tree from a list of leaf node hashes.-buildMerkleRoot :: [Hash256] -- ^ List of transaction hashes (leaf nodes).- -> Hash256 -- ^ Root of the merkle tree.+buildMerkleRoot :: [TxHash] -- ^ List of transaction hashes (leaf nodes).+ -> MerkleRoot -- ^ Root of the merkle tree. buildMerkleRoot txs = calcHash (calcTreeHeight $ length txs) 0 txs -hash2 :: Hash256 -> Hash256 -> Hash256+hash2 :: Word256 -> Word256 -> Word256 hash2 a b = doubleHash256 $ encode' a `BS.append` encode' b -- | Computes the hash of a specific node in a merkle tree. calcHash :: Int -- ^ Height of the node in the merkle tree. -> Int -- ^ Position of the node (0 for the leftmost node).- -> [Hash256] -- ^ Transaction hashes of the merkle tree (leaf nodes).- -> Hash256 -- ^ Hash of the node at the specified position.+ -> [TxHash] -- ^ Transaction hashes of the merkle tree (leaf nodes).+ -> Word256 -- ^ Hash of the node at the specified position. calcHash height pos txs | height < 0 || pos < 0 = error "calcHash: Invalid parameters"- | height == 0 = txs !! pos+ | height == 0 = fromIntegral $ txs !! pos | otherwise = hash2 left right where left = calcHash (height-1) (pos*2) txs@@ -51,16 +57,17 @@ -- | Build a partial merkle tree. buildPartialMerkle - :: [(Hash256,Bool)] + :: [(TxHash,Bool)] -- ^ List of transactions hashes forming the leaves of the merkle tree -- and a bool indicating if that transaction should be included in the -- partial merkle tree.- -> ([Bool], [Hash256]) + -> (FlagBits, PartialMerkleTree) -- ^ Flag bits (used to parse the partial merkle tree) and the -- partial merkle tree. buildPartialMerkle hs = traverseAndBuild (calcTreeHeight $ length hs) 0 hs -traverseAndBuild :: Int -> Int -> [(Hash256,Bool)] -> ([Bool], [Hash256])+traverseAndBuild :: Int -> Int -> [(TxHash,Bool)] + -> (FlagBits, PartialMerkleTree) traverseAndBuild height pos txs | height < 0 || pos < 0 = error "traverseAndBuild: Invalid parameters" | height == 0 || not match = ([match],[calcHash height pos t])@@ -75,8 +82,8 @@ = traverseAndBuild (height-1) (pos*2+1) txs | otherwise = ([],[]) -traverseAndExtract :: Int -> Int -> Int -> [Bool] -> [Hash256] - -> Maybe (Hash256, [Hash256], Int, Int)+traverseAndExtract :: Int -> Int -> Int -> FlagBits -> PartialMerkleTree+ -> Maybe (MerkleRoot, [TxHash], Int, Int) traverseAndExtract height pos ntx flags hashes | length flags == 0 = Nothing | height == 0 || not match = leafResult@@ -89,7 +96,8 @@ where leafResult | null hashes = Nothing- | otherwise = Just (h,if height == 0 && match then [h] else [],1,1)+ | otherwise = Just + (h,if height == 0 && match then [fromIntegral h] else [],1,1) (match:fs) = flags (h:_) = hashes leftM = traverseAndExtract (height-1) (pos*2) ntx fs hashes@@ -101,10 +109,10 @@ -- | Extracts the matching hashes from a partial merkle tree. This will return -- the list of transaction hashes that have been included (set to True) in -- a call to 'buildPartialMerkle'.-extractMatches :: [Bool] -- ^ Flag bits (produced by buildPartialMerkle).- -> [Hash256] -- ^ Partial merkle tree.- -> Int -- ^ Number of transaction at height 0 (leaf nodes).- -> Either String (Hash256, [Hash256])+extractMatches :: FlagBits -- ^ Flag bits (produced by buildPartialMerkle).+ -> PartialMerkleTree -- ^ Partial merkle tree.+ -> Int -- ^ Number of transaction at height 0 (leaf nodes).+ -> Either String (MerkleRoot, [TxHash]) -- ^ Merkle root and the list of matching transaction hashes. extractMatches flags hashes ntx | ntx == 0 = Left $@@ -125,6 +133,5 @@ where resM = traverseAndExtract (calcTreeHeight ntx) 0 ntx flags hashes (merkleRoot, matches, nBitsUsed, nHashUsed) = fromJust resM-
Network/Haskoin/Crypto/Mnemonic.hs view
@@ -1,10 +1,10 @@ {-# LANGUAGE OverloadedStrings #-}--- | Mnemonic keys (BIP-0039)+-- | Mnemonic keys (BIP-39). Only the English dictionary in the BIP is+-- supported. module Network.Haskoin.Crypto.Mnemonic ( -- * Data types- WordList-, Entropy+ Entropy , Mnemonic , Passphrase , Seed@@ -14,12 +14,8 @@ , fromMnemonic -- * Generating 512-bit seeds-, anyToSeed , mnemonicToSeed - -- * Dictionaries-, english- -- * Helper functions , getBits ) where@@ -30,53 +26,55 @@ import Data.Bits ((.&.), shiftL, shiftR) import Data.ByteString (ByteString) import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as B8+import Data.Char (isAscii) import Data.List import Data.Maybe-import Data.Text (Text)-import qualified Data.Text as T-import Data.Text.Encoding (encodeUtf8)-import Data.Text.ICU.Normalize (NormalizationMode(NFKD), normalize) import Network.Haskoin.Util (bsToInteger, integerToBS) -type WordList = [Text] type Entropy = ByteString-type Mnemonic = Text-type Passphrase = Text+type Mnemonic = String+type Passphrase = String type Seed = ByteString type Checksum = ByteString --- | Provide dictionary and intial entropy as a 'ByteString' with length--- multiple of 32 bits (4 bytes). Output a mnemonic sentence as 'Text'.-toMnemonic :: WordList -> Entropy -> Either String Mnemonic-toMnemonic wl ent = do- when (remainder /= 0)- $ Left "toMnemonic: entropy must be multiples of 32 bits"- when (cs_len > 16)- $ Left "toMnemonic: maximum entropy is 512 bits"+-- | Provide intial entropy as a 'ByteString' of length multiple of 4 bytes.+-- Output a mnemonic sentence.+toMnemonic :: Entropy -> Either String Mnemonic+toMnemonic ent = do+ when (remainder /= 0) $ + Left "toMnemonic: entropy must be multiples of 4 bytes"+ when (cs_len > 16) $ + Left "toMnemonic: maximum entropy is 64 bytes (512 bits)"+ when (isJust $ find (not . isAscii) ms) $+ Left "fromMnemonic: non-ASCII characters not supported" return ms where (cs_len, remainder) = BS.length ent `quotRem` 4 cs = calcCS cs_len ent indices = bsToIndices $ ent `BS.append` cs- ms = T.unwords $ map (wl !!) indices+ ms = unwords $ map (wl !!) indices -- | Revert 'toMnemonic'. Do not use this to generate seeds. Instead use--- 'mnemonicToSeed'.-fromMnemonic :: WordList -> Mnemonic -> Either String Entropy-fromMnemonic wl ms = do- when (word_count > 48)- . Left $ "fromMnemonic: too many words: " ++ show word_count- when (word_count `mod` 3 /= 0)- . Left $ "fromMnemonic: wrong number of words: " ++ show word_count- ms_bs <- indicesToBS =<< getIndices wl ms_words+-- 'mnemonicToSeed'. This outputs the original entropy used to generate a+-- mnemonic.+fromMnemonic :: Mnemonic -> Either String Entropy+fromMnemonic ms = do+ when (isJust $ find (not . isAscii) ms) $+ Left "fromMnemonic: non-ASCII characters not supported"+ when (word_count > 48) $ + Left $ "fromMnemonic: too many words: " ++ show word_count+ when (word_count `mod` 3 /= 0) $ + Left $ "fromMnemonic: wrong number of words: " ++ show word_count+ ms_bs <- indicesToBS =<< getIndices ms_words let (ms_ent, ms_cs) = BS.splitAt (ent_len * 4) ms_bs ms_cs_num = numCS cs_len ms_cs ent_cs_num = numCS cs_len $ calcCS cs_len ms_ent- when (ent_cs_num /= ms_cs_num)- . Left $ "fromMnemonic: checksum failed: " ++ sh ent_cs_num ms_cs_num+ when (ent_cs_num /= ms_cs_num) $ + Left $ "fromMnemonic: checksum failed: " ++ sh ent_cs_num ms_cs_num return ms_ent where- ms_words = T.words $ normalize NFKD ms+ ms_words = words ms word_count = length ms_words (ent_len, cs_len) = (word_count * 11) `quotRem` 32 sh cs_a cs_b = show cs_a ++ " /= " ++ show cs_b@@ -91,23 +89,20 @@ 0 -> id x -> flip shiftR x --- | Turn any sequence of characters to a seed. Does not have to be a mnemonic--- sentence generated from 'toMnemonic'. Use 'mnemonicToSeed' to get a seed--- from a mnemonic sentence.+-- | Turn any sequence of characters into a 512-bit seed. Does not have to be+-- a mnemonic sentence generated from 'toMnemonic'. Use 'mnemonicToSeed' to+-- get a seed from a mnemonic sentence. Warning: Does not perform NFKD+-- normalization. anyToSeed :: Passphrase -> Mnemonic -> Seed-anyToSeed pf ms = sha512PBKDF2 p s 2048 64- where- p = encodeUtf8 $ normalize NFKD ms- s = encodeUtf8 $ normalize NFKD ("mnemonic" `T.append` pf)+anyToSeed pf ms = sha512PBKDF2 (B8.pack ms) (B8.pack $ "mnemonic" ++ pf) 2048 64 --- | Get a seed from a mnemonic sentence. Will calculate checksum. Requires--- same dictionary used to generate the mnemonic sentence as first argument.--- Passphrase can be used to protect the mnemonic. Use an empty string as+-- | Get a 512-bit seed from a mnemonic sentence. Will calculate checksum.+-- Passphrase can be used to protect the mnemonic. Use an empty string as -- passphrase if none is required.-mnemonicToSeed :: WordList -> Passphrase -> Mnemonic -> Either String Seed-mnemonicToSeed wl pf ms = do- ent <- fromMnemonic wl ms- mnm <- toMnemonic wl ent+mnemonicToSeed :: Passphrase -> Mnemonic -> Either String Seed+mnemonicToSeed pf ms = do+ ent <- fromMnemonic ms+ mnm <- toMnemonic ent return $ anyToSeed pf mnm -- | Obtain 'Int' bits from beginning of 'ByteString'. Resulting 'ByteString'@@ -123,15 +118,15 @@ i = BS.init s l = BS.last s .&. (0xff `shiftL` (8 - r)) -- zero unneeded bits --- Expects already normalized list of words-getIndices :: WordList -> [Text] -> Either String [Int]-getIndices wl ws- | null n = return . fromJust $ sequence i- | otherwise = Left $ "getIndices: words not found: " ++ T.unpack w+-- | Get indices of words in word list.+getIndices :: [String] -> Either String [Int]+getIndices ws+ | null n = return $ catMaybes i+ | otherwise = Left $ "getIndices: words not found: " ++ w where i = map (flip elemIndex wl) ws n = elemIndices Nothing i- w = T.unwords $ map (ws !!) n+ w = unwords $ map (ws !!) n indicesToBS :: [Int] -> Either String ByteString indicesToBS is = do@@ -152,9 +147,9 @@ go 0 _ = [] go n i = (fromIntegral $ i `mod` 2048) : go (n - 1) (i `shiftR` 11) --- | Standard English dictionary from BIP-0039 specification.-english :: [Text]-english =+-- | Standard English dictionary from BIP-39 specification.+wl :: [String]+wl = [ "abandon", "ability", "able", "about", "above", "absent" , "absorb", "abstract", "absurd", "abuse", "access", "accident" , "account", "accuse", "achieve", "acid", "acoustic", "acquire"
Network/Haskoin/Crypto/NormalizedKeys.hs view
@@ -38,6 +38,7 @@ , intMulSigAddrs ) where +import Control.DeepSeq (NFData, rnf) import Control.Monad (liftM2, guard) import Control.Applicative ((<$>)) @@ -57,6 +58,9 @@ newtype MasterKey = MasterKey { masterKey :: XPrvKey } deriving (Eq, Show, Read) +instance NFData MasterKey where+ rnf (MasterKey m) = rnf m+ -- | Data type representing a private account key. Account keys are generated -- from a 'MasterKey' through prime derivation. This guarantees that the -- 'MasterKey' will not be compromised if the account key is compromised. @@ -64,6 +68,9 @@ newtype AccPrvKey = AccPrvKey { getAccPrvKey :: XPrvKey } deriving (Eq, Show, Read) +instance NFData AccPrvKey where+ rnf (AccPrvKey k) = rnf k+ -- | Data type representing a public account key. It is computed through -- derivation from an 'AccPrvKey'. It can not be derived from the 'MasterKey' -- directly (property of prime derivation). It is represented as M\/i'\/ in@@ -72,6 +79,9 @@ newtype AccPubKey = AccPubKey { getAccPubKey :: XPubKey } deriving (Eq, Show, Read) +instance NFData AccPubKey where+ rnf (AccPubKey k) = rnf k+ -- | Data type representing a private address key. Private address keys are -- generated through a non-prime derivation from an 'AccPrvKey'. Non-prime -- derivation is used so that the public account key can generate the receiving@@ -83,6 +93,9 @@ newtype AddrPrvKey = AddrPrvKey { getAddrPrvKey :: XPrvKey } deriving (Eq, Show, Read) +instance NFData AddrPrvKey where+ rnf (AddrPrvKey k) = rnf k+ -- | Data type representing a public address key. They are generated through -- non-prime derivation from an 'AccPubKey'. This is a useful feature for -- read-only wallets. They are represented as M\/i'\/0\/j in BIP32 notation@@ -91,6 +104,9 @@ newtype AddrPubKey = AddrPubKey { getAddrPubKey :: XPubKey } deriving (Eq, Show, Read) +instance NFData AddrPubKey where+ rnf (AddrPubKey k) = rnf k+ -- | Create a 'MasterKey' from a seed. makeMasterKey :: BS.ByteString -> Maybe MasterKey makeMasterKey bs = MasterKey <$> makeXPrvKey bs@@ -198,35 +214,35 @@ addr :: AddrPubKey -> Address addr = xPubAddr . getAddrPubKey --- | Computes an external base58 address from an 'AccPubKey' and a +-- | Computes an external address from an 'AccPubKey' and a -- derivation index.-extAddr :: AccPubKey -> KeyIndex -> Maybe String-extAddr a i = addrToBase58 . addr <$> extPubKey a i+extAddr :: AccPubKey -> KeyIndex -> Maybe Address+extAddr a i = addr <$> extPubKey a i --- | Computes an internal base58 addres from an 'AccPubKey' and a +-- | Computes an internal addres from an 'AccPubKey' and a -- derivation index.-intAddr :: AccPubKey -> KeyIndex -> Maybe String-intAddr a i = addrToBase58 . addr <$> intPubKey a i+intAddr :: AccPubKey -> KeyIndex -> Maybe Address+intAddr a i = addr <$> intPubKey a i --- | Cyclic list of all external base58 addresses derived from a 'AccPubKey'+-- | Cyclic list of all external addresses derived from a 'AccPubKey' -- and starting from an offset index.-extAddrs :: AccPubKey -> KeyIndex -> [(String,KeyIndex)]+extAddrs :: AccPubKey -> KeyIndex -> [(Address,KeyIndex)] extAddrs a i = mapMaybe f $ cycleIndex i where f j = liftM2 (,) (extAddr a j) (return j) --- | Cyclic list of all internal base58 addresses derived from a 'AccPubKey'+-- | Cyclic list of all internal addresses derived from a 'AccPubKey' -- and starting from an offset index.-intAddrs :: AccPubKey -> KeyIndex -> [(String,KeyIndex)]+intAddrs :: AccPubKey -> KeyIndex -> [(Address,KeyIndex)] intAddrs a i = mapMaybe f $ cycleIndex i where f j = liftM2 (,) (intAddr a j) (return j) -- | Same as 'extAddrs' with the list reversed.-extAddrs' :: AccPubKey -> KeyIndex -> [(String,KeyIndex)]+extAddrs' :: AccPubKey -> KeyIndex -> [(Address,KeyIndex)] extAddrs' a i = mapMaybe f $ cycleIndex' i where f j = liftM2 (,) (extAddr a j) (return j) -- | Same as 'intAddrs' with the list reversed.-intAddrs' :: AccPubKey -> KeyIndex -> [(String,KeyIndex)]+intAddrs' :: AccPubKey -> KeyIndex -> [(Address,KeyIndex)] intAddrs' a i = mapMaybe f $ cycleIndex' i where f j = liftM2 (,) (intAddr a j) (return j) @@ -260,33 +276,33 @@ intMulSigKeys a ps i = mapMaybe f $ cycleIndex i where f j = liftM2 (,) (intMulSigKey a ps j) (return j) --- | Computes an external base58 multisig address from an 'AccPubKey', a+-- | Computes an external multisig address from an 'AccPubKey', a -- list of thirdparty multisig keys and a derivation index.-extMulSigAddr :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> Maybe String+extMulSigAddr :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> Maybe Address extMulSigAddr a ps r i = do xs <- (map (xPubKey . getAddrPubKey)) <$> extMulSigKey a ps i- return $ addrToBase58 $ scriptAddr $ sortMulSig $ PayMulSig xs r+ return $ scriptAddr $ sortMulSig $ PayMulSig xs r --- | Computes an internal base58 multisig address from an 'AccPubKey', a+-- | Computes an internal multisig address from an 'AccPubKey', a -- list of thirdparty multisig keys and a derivation index.-intMulSigAddr :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> Maybe String+intMulSigAddr :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> Maybe Address intMulSigAddr a ps r i = do xs <- (map (xPubKey . getAddrPubKey)) <$> intMulSigKey a ps i- return $ addrToBase58 $ scriptAddr $ sortMulSig $ PayMulSig xs r+ return $ scriptAddr $ sortMulSig $ PayMulSig xs r --- | Cyclic list of all external base58 multisig addresses derived from+-- | Cyclic list of all external multisig addresses derived from -- an 'AccPubKey' and a list of thirdparty multisig keys. The list starts -- at an offset index. extMulSigAddrs :: AccPubKey -> [XPubKey] -> Int -> KeyIndex - -> [(String,KeyIndex)]+ -> [(Address,KeyIndex)] extMulSigAddrs a ps r i = mapMaybe f $ cycleIndex i where f j = liftM2 (,) (extMulSigAddr a ps r j) (return j) --- | Cyclic list of all internal base58 multisig addresses derived from+-- | Cyclic list of all internal multisig addresses derived from -- an 'AccPubKey' and a list of thirdparty multisig keys. The list starts -- at an offset index. intMulSigAddrs :: AccPubKey -> [XPubKey] -> Int -> KeyIndex - -> [(String,KeyIndex)]+ -> [(Address,KeyIndex)] intMulSigAddrs a ps r i = mapMaybe f $ cycleIndex i where f j = liftM2 (,) (intMulSigAddr a ps r j) (return j)
Network/Haskoin/Crypto/Point.hs view
@@ -15,6 +15,7 @@ import Data.Bits (shiftR) import Control.Applicative ((<$>))+import Control.DeepSeq (NFData, rnf) import Network.Haskoin.Crypto.Curve import Network.Haskoin.Crypto.BigWord @@ -33,6 +34,10 @@ -} data Point = Point !FieldP !FieldP !FieldP | InfPoint deriving (Show, Read)++instance NFData Point where+ rnf (Point p1 p2 p3) = rnf p1 `seq` rnf p2 `seq` rnf p3+ rnf x = x `seq` () instance Eq Point where InfPoint == InfPoint = True
+ Network/Haskoin/Internals.hs view
@@ -0,0 +1,47 @@+{-|+ This module expose haskoin internals. No guarantee is made on the+ stability of the interface of these internal modules.+-}+module Network.Haskoin.Internals+( module Network.Haskoin.Crypto.NumberTheory+, module Network.Haskoin.Crypto.Curve+, module Network.Haskoin.Crypto.Hash+, module Network.Haskoin.Crypto.BigWord+, module Network.Haskoin.Crypto.Point+, module Network.Haskoin.Crypto.Base58+, module Network.Haskoin.Crypto.Keys+, module Network.Haskoin.Crypto.ExtendedKeys+, module Network.Haskoin.Crypto.NormalizedKeys+, module Network.Haskoin.Crypto.ECDSA+, module Network.Haskoin.Crypto.Mnemonic+, module Network.Haskoin.Crypto.Merkle+, module Network.Haskoin.Crypto.Bloom+, module Network.Haskoin.Protocol.Message+, module Network.Haskoin.Protocol.Types+, module Network.Haskoin.Script.Types+, module Network.Haskoin.Script.Parser+, module Network.Haskoin.Script.SigHash+, module Network.Haskoin.Script.Evaluator+, module Network.Haskoin.Transaction.Builder+) where++import Network.Haskoin.Crypto.NumberTheory+import Network.Haskoin.Crypto.Curve+import Network.Haskoin.Crypto.Hash+import Network.Haskoin.Crypto.BigWord+import Network.Haskoin.Crypto.Point+import Network.Haskoin.Crypto.Base58+import Network.Haskoin.Crypto.Keys+import Network.Haskoin.Crypto.ExtendedKeys+import Network.Haskoin.Crypto.NormalizedKeys+import Network.Haskoin.Crypto.ECDSA+import Network.Haskoin.Crypto.Mnemonic+import Network.Haskoin.Crypto.Merkle+import Network.Haskoin.Crypto.Bloom+import Network.Haskoin.Protocol.Message+import Network.Haskoin.Protocol.Types+import Network.Haskoin.Script.Types+import Network.Haskoin.Script.Parser+import Network.Haskoin.Script.SigHash+import Network.Haskoin.Script.Evaluator+import Network.Haskoin.Transaction.Builder
Network/Haskoin/Protocol.hs view
@@ -16,7 +16,6 @@ , GetHeaders(..) , Headers(..) , BlockHeaderCount-, blockid -- * Requesting data , GetData(..)@@ -25,28 +24,12 @@ , InvType(..) , NotFound(..) - -- *Scripts- -- | More informations on scripts is available here:- -- <http://en.bitcoin.it/wiki/Script>-, Script(..)-, ScriptOp(..)-, PushDataType(..)-, opPushData-, getScriptOps-, putScriptOps-, decodeScriptOps-, encodeScriptOps- -- *Transactions , Tx(..)-, txid-, cbid , CoinbaseTx(..) , TxIn(..) , TxOut(..) , OutPoint(..)-, encodeTxid-, decodeTxid -- * Merkle trees and bloom filters , MerkleBlock(..)@@ -67,6 +50,9 @@ , Ping(..) , Pong(..) , Alert(..)+, Reject(..)+, RejectCode(..)+, reject -- *Messages , Message(..)@@ -75,25 +61,5 @@ ) where import Network.Haskoin.Protocol.Message-import Network.Haskoin.Protocol.Addr-import Network.Haskoin.Protocol.Alert-import Network.Haskoin.Protocol.BlockHeader-import Network.Haskoin.Protocol.Block-import Network.Haskoin.Protocol.MerkleBlock-import Network.Haskoin.Protocol.GetBlocks-import Network.Haskoin.Protocol.GetData-import Network.Haskoin.Protocol.GetHeaders-import Network.Haskoin.Protocol.Headers-import Network.Haskoin.Protocol.Inv-import Network.Haskoin.Protocol.InvVector-import Network.Haskoin.Protocol.MessageHeader-import Network.Haskoin.Protocol.NetworkAddress-import Network.Haskoin.Protocol.NotFound-import Network.Haskoin.Protocol.Ping-import Network.Haskoin.Protocol.Script-import Network.Haskoin.Protocol.Tx-import Network.Haskoin.Protocol.VarInt-import Network.Haskoin.Protocol.VarString-import Network.Haskoin.Protocol.Version-import Network.Haskoin.Protocol.BloomFilter+import Network.Haskoin.Protocol.Types
− Network/Haskoin/Protocol/Addr.hs
@@ -1,39 +0,0 @@-module Network.Haskoin.Protocol.Addr -( Addr(..)-, NetworkAddressTime -) where--import Control.Monad (liftM2, replicateM, forM_)-import Control.Applicative ((<$>))--import Data.Word (Word32)-import Data.Binary (Binary, get, put)-import Data.Binary.Get (getWord32le)-import Data.Binary.Put (putWord32le)--import Network.Haskoin.Protocol.VarInt-import Network.Haskoin.Protocol.NetworkAddress---- | Network address with a timestamp-type NetworkAddressTime = (Word32, NetworkAddress)---- | Provides information on known nodes in the bitcoin network. An 'Addr'--- type is sent inside a 'Message' as a response to a 'GetAddr' message.-data Addr = - Addr { - -- List of addresses of other nodes on the network with timestamps.- addrList :: ![NetworkAddressTime] - } - deriving (Eq, Show, Read)--instance Binary Addr where-- get = Addr <$> (repList =<< get)- where - repList (VarInt c) = replicateM (fromIntegral c) action- action = liftM2 (,) getWord32le get -- put (Addr xs) = do- put $ VarInt $ fromIntegral $ length xs- forM_ xs $ \(a,b) -> (putWord32le a) >> (put b)-
− Network/Haskoin/Protocol/Alert.hs
@@ -1,21 +0,0 @@-module Network.Haskoin.Protocol.Alert ( Alert(..) ) where--import Control.Applicative ((<$>),(<*>))-import Data.Binary (Binary, get, put)-import Network.Haskoin.Protocol.VarString---- | Data type describing signed messages that can be sent between bitcoin--- nodes to display important notifications to end users about the health of--- the network.-data Alert = - Alert {- -- | Alert payload. - alertPayload :: !VarString- -- | ECDSA signature of the payload- , alertSignature :: !VarString- } deriving (Eq, Show, Read)--instance Binary Alert where- get = Alert <$> get <*> get- put (Alert p s) = put p >> put s-
− Network/Haskoin/Protocol/Block.hs
@@ -1,38 +0,0 @@-module Network.Haskoin.Protocol.Block (Block(..)) where--import Control.Monad (replicateM, forM_)--import Data.Binary (Binary, get, put)--import Network.Haskoin.Protocol.Tx-import Network.Haskoin.Protocol.VarInt-import Network.Haskoin.Protocol.BlockHeader---- | Data type describing a block in the bitcoin protocol. Blocks are sent in--- response to 'GetData' messages that are requesting information from a--- block hash.-data Block = - Block {- -- | Header information for this block.- blockHeader :: !BlockHeader- -- | Coinbase transaction of this block.- , blockCoinbaseTx :: !CoinbaseTx- -- | List of transactions pertaining to this block.- , blockTxns :: ![Tx]- } deriving (Eq, Show)--instance Binary Block where-- get = do- header <- get- (VarInt c) <- get- cb <- get- txs <- replicateM (fromIntegral (c-1)) get- return $ Block header cb txs-- put (Block h cb txs) = do- put h- put $ VarInt $ fromIntegral $ (length txs) + 1- put cb- forM_ txs put-
− Network/Haskoin/Protocol/BlockHeader.hs
@@ -1,63 +0,0 @@-module Network.Haskoin.Protocol.BlockHeader -( BlockHeader(..) -, blockid-) where--import Control.Applicative ((<$>),(<*>))--import Data.Word (Word32)-import Data.Binary (Binary, get, put)-import Data.Binary.Get (getWord32le)-import Data.Binary.Put (putWord32le)--import Network.Haskoin.Crypto.Hash -import Network.Haskoin.Util ---- | Data type recording information on a 'Block'. The hash of a block is--- defined as the hash of this data structure. The block mining process--- involves finding a partial hash collision by varying the nonce in the--- 'BlockHeader' and/or additional randomness in the 'CoinbaseTx' of this--- 'Block'. Variations in the 'CoinbaseTx' will result in different merkle --- roots in the 'BlockHeader'.-data BlockHeader = - BlockHeader {- -- | Block version information, based on the version of the- -- software creating this block.- blockVersion :: !Word32- -- | Hash of the previous block (parent) referenced by this- -- block.- , prevBlock :: !Hash256- -- | Root of the merkle tree of all transactions pertaining- -- to this block.- , merkleRoot :: !Hash256- -- | Unix timestamp recording when this block was created- , blockTimestamp :: !Word32- -- | The difficulty target being used for this block- , blockBits :: !Word32- -- | A random nonce used to generate this block. Additional- -- randomness is included in the coinbase transaction of- -- this block.- , bhNonce :: !Word32- } deriving (Eq, Show, Read)--instance Binary BlockHeader where-- get = BlockHeader <$> getWord32le- <*> get- <*> get- <*> getWord32le- <*> getWord32le- <*> getWord32le-- put (BlockHeader v p m bt bb n) = do- putWord32le v- put p- put m- putWord32le bt- putWord32le bb- putWord32le n ---- | Compute the hash of a block header-blockid :: BlockHeader -> Hash256-blockid = doubleHash256 . encode'-
− Network/Haskoin/Protocol/BloomFilter.hs
@@ -1,101 +0,0 @@-module Network.Haskoin.Protocol.BloomFilter -( BloomFlags(..)-, BloomFilter(..)-, FilterLoad(..)-, FilterAdd(..)-) where--import Control.Monad (replicateM, forM_)-import Control.Applicative ((<$>),(<*>))--import Data.Word (Word8, Word32)-import Data.Binary (Binary, get, put)-import Data.Binary.Get (getWord8, getWord32le, getByteString)-import Data.Binary.Put (putWord8, putWord32le, putByteString)-import qualified Data.Foldable as F (toList)-import qualified Data.Sequence as S (Seq, fromList, length)-import qualified Data.ByteString as BS (ByteString, length)--import Network.Haskoin.Protocol.VarInt---- | The bloom flags are used to tell the remote peer how to auto-update--- the provided bloom filter. -data BloomFlags- = BloomUpdateNone -- ^ Never update- | BloomUpdateAll -- ^ Auto-update on all outputs- | BloomUpdateP2PubKeyOnly - -- ^ Only auto-update on outputs that are pay-to-pubkey or pay-to-multisig.- -- This is the default setting.- deriving (Eq, Show, Read)--instance Binary BloomFlags where- get = go =<< getWord8- where- go 0 = return BloomUpdateNone- go 1 = return BloomUpdateAll- go 2 = return BloomUpdateP2PubKeyOnly- go _ = fail "BloomFlags get: Invalid bloom flag"-- put f = putWord8 $ case f of- BloomUpdateNone -> 0- BloomUpdateAll -> 1- BloomUpdateP2PubKeyOnly -> 2- --- | A bloom filter is a probabilistic data structure that SPV clients send to--- other peers to filter the set of transactions received from them. Bloom--- filters are probabilistic and have a false positive rate. Some transactions--- that pass the filter may not be relevant to the receiving peer. By--- controlling the false positive rate, SPV nodes can trade off bandwidth--- versus privacy.-data BloomFilter = BloomFilter- { bloomData :: S.Seq Word8 -- ^ Bloom filter data- , bloomFull :: Bool - -- ^ Flag indicating if the filter is full ('bloomData' is all 0x00)- , bloomEmpty :: Bool- -- ^ Flag indicating if the filter is empty ('bloomData' is all 0xff)- , bloomHashFuncs :: Word32 -- ^ Number of hash functions for this filter- , bloomTweak :: Word32 -- ^ Hash function random nonce- , bloomFlags :: BloomFlags -- ^ Bloom filter auto-update flags- }- deriving (Eq, Show, Read)--instance Binary BloomFilter where-- get = BloomFilter <$> (S.fromList <$> (readDat =<< get))- <*> (return False) <*> (return False)- <*> getWord32le <*> getWord32le- <*> get- where- readDat (VarInt len) = replicateM (fromIntegral len) getWord8 -- put (BloomFilter dat _ _ hashFuncs tweak flags) = do- put $ VarInt $ fromIntegral $ S.length dat- forM_ (F.toList dat) putWord8- putWord32le hashFuncs- putWord32le tweak- put flags---- | Set a new bloom filter on the peer connection.-newtype FilterLoad = FilterLoad { getBloomFilter :: BloomFilter }- deriving (Eq, Show, Read)--instance Binary FilterLoad where- get = FilterLoad <$> get- put (FilterLoad f) = put f---- | Add the given data element to the connections current filter without--- requiring a completely new one to be set.-newtype FilterAdd = FilterAdd { getFilterData :: BS.ByteString }- deriving (Eq, Show, Read)--instance Binary FilterAdd where- get = do- (VarInt len) <- get- dat <- getByteString $ fromIntegral len- return $ FilterAdd dat-- put (FilterAdd bs) = do- put $ VarInt $ fromIntegral $ BS.length bs- putByteString bs--
− Network/Haskoin/Protocol/GetBlocks.hs
@@ -1,53 +0,0 @@-module Network.Haskoin.Protocol.GetBlocks -( GetBlocks(..) -, BlockLocator-) where--import Control.Monad (replicateM, forM_)-import Control.Applicative ((<$>),(<*>))--import Data.Word (Word32)-import Data.Binary (Binary, get, put)-import Data.Binary.Get (getWord32le)-import Data.Binary.Put (putWord32le)--import Network.Haskoin.Protocol.VarInt-import Network.Haskoin.Crypto.Hash --type BlockLocator = [Hash256]---- | Data type representing a GetBlocks message request. It is used in the--- bitcoin protocol to retrieve blocks from a peer by providing it a--- 'BlockLocator' object. The 'BlockLocator' is a sparse list of block hashes--- from the caller node with the purpose of informing the receiving node--- about the state of the caller's blockchain. The receiver node will detect--- a wrong branch in the caller's main chain and send the caller appropriate --- 'Blocks'. The response to a 'GetBlocks' message is an 'Inv' message--- containing the list of block hashes pertaining to the request. -data GetBlocks = - GetBlocks {- -- | The protocol version- getBlocksVersion :: !Word32- -- | Block locator object. It is a list of block hashes from the- -- most recent block back to the genesis block. The list is- -- dense at first and sparse towards the end.- , getBlocksLocator :: !BlockLocator- -- | Hash of the last desired block. If set to zero, the- -- maximum number of block hashes is returned (500).- , getBlocksHashStop :: !Hash256- } deriving (Eq, Show, Read)--instance Binary GetBlocks where-- get = GetBlocks <$> getWord32le- <*> (repList =<< get)- <*> get- where - repList (VarInt c) = replicateM (fromIntegral c) get-- put (GetBlocks v xs h) = do- putWord32le v- put $ VarInt $ fromIntegral $ length xs- forM_ xs put- put h-
− Network/Haskoin/Protocol/GetData.hs
@@ -1,34 +0,0 @@-module Network.Haskoin.Protocol.GetData ( GetData(..) ) where--import Control.Monad (replicateM, forM_)-import Control.Applicative ((<$>))--import Data.Binary (Binary, get, put)--import Network.Haskoin.Protocol.InvVector-import Network.Haskoin.Protocol.VarInt---- | The 'GetData' type is used to retrieve information on a specific object--- ('Block' or 'Tx') identified by the objects hash. The payload of a 'GetData'--- request is a list of 'InvVector' which represent all the hashes for which a--- node wants to request information. The response to a 'GetBlock' message--- wille be either a 'Block' or a 'Tx' message depending on the type of the--- object referenced by the hash. Usually, 'GetData' messages are sent after a--- node receives an 'Inv' message to obtain information on unknown object--- hashes. -data GetData = - GetData {- -- | List of object hashes - getDataList :: ![InvVector] - } deriving (Eq, Show, Read)--instance Binary GetData where-- get = GetData <$> (repList =<< get)- where - repList (VarInt c) = replicateM (fromIntegral c) get-- put (GetData xs) = do- put $ VarInt $ fromIntegral $ length xs- forM_ xs put-
− Network/Haskoin/Protocol/GetHeaders.hs
@@ -1,46 +0,0 @@-module Network.Haskoin.Protocol.GetHeaders ( GetHeaders(..) ) where--import Control.Monad (replicateM, forM_)-import Control.Applicative ((<$>),(<*>))--import Data.Word (Word32)-import Data.Binary (Binary, get, put)-import Data.Binary.Get (getWord32le)-import Data.Binary.Put (putWord32le)--import Network.Haskoin.Protocol.VarInt-import Network.Haskoin.Protocol.GetBlocks -import Network.Haskoin.Crypto.Hash ---- | Similar to the 'GetBlocks' message type but for retrieving block headers--- only. The response to a 'GetHeaders' request is a 'Headers' message--- containing a list of block headers pertaining to the request. A maximum of--- 2000 block headers can be returned. 'GetHeaders' is used by thin (SPV)--- clients to exclude block contents when synchronizing the blockchain.-data GetHeaders = - GetHeaders {- -- | The protocol version- getHeadersVersion :: !Word32- -- | Block locator object. It is a list of block hashes from- -- the most recent block back to the Genesis block. The list- -- is dense at first and sparse towards the end.- , getHeadersBL :: !BlockLocator- -- | Hash of the last desired block header. When set to zero,- -- the maximum number of block headers is returned (2000)- , getHeadersHashStop :: !Hash256- } deriving (Eq, Show, Read)--instance Binary GetHeaders where-- get = GetHeaders <$> getWord32le- <*> (repList =<< get)- <*> get- where - repList (VarInt c) = replicateM (fromIntegral c) get-- put (GetHeaders v xs h) = do- putWord32le v- put $ VarInt $ fromIntegral $ length xs- forM_ xs put- put h-
− Network/Haskoin/Protocol/Headers.hs
@@ -1,36 +0,0 @@-module Network.Haskoin.Protocol.Headers -( Headers(..)-, BlockHeaderCount-) where--import Control.Monad (liftM2, replicateM, forM_)-import Control.Applicative ((<$>))--import Data.Binary (Binary, get, put)--import Network.Haskoin.Protocol.VarInt-import Network.Haskoin.Protocol.BlockHeader---- | 'BlockHeader' type with a transaction count as 'VarInt'-type BlockHeaderCount = (BlockHeader, VarInt)---- | The 'Headers' type is used to return a list of block headers in--- response to a 'GetHeaders' message.-data Headers = - Headers { - -- | List of block headers with respective transaction counts- headersList :: ![BlockHeaderCount] - } - deriving (Eq, Show, Read)--instance Binary Headers where-- get = Headers <$> (repList =<< get)- where - repList (VarInt c) = replicateM (fromIntegral c) action- action = liftM2 (,) get get-- put (Headers xs) = do- put $ VarInt $ fromIntegral $ length xs- forM_ xs $ \(a,b) -> put a >> put b-
− Network/Haskoin/Protocol/Inv.hs
@@ -1,29 +0,0 @@-module Network.Haskoin.Protocol.Inv ( Inv(..) ) where--import Control.Monad (replicateM, forM_)-import Control.Applicative ((<$>))--import Data.Binary (Binary, get, put)--import Network.Haskoin.Protocol.InvVector-import Network.Haskoin.Protocol.VarInt---- | 'Inv' messages are used by nodes to advertise their knowledge of new--- objects by publishing a list of hashes. 'Inv' messages can be sent--- unsolicited or in response to a 'GetBlocks' message.-data Inv = - Inv { - -- | Inventory vectors- invList :: ![InvVector] - } deriving (Eq, Show, Read)--instance Binary Inv where-- get = Inv <$> (repList =<< get)- where - repList (VarInt c) = replicateM (fromIntegral c) get-- put (Inv xs) = do- put $ VarInt $ fromIntegral $ length xs- forM_ xs put-
− Network/Haskoin/Protocol/InvVector.hs
@@ -1,54 +0,0 @@-module Network.Haskoin.Protocol.InvVector -( InvVector(..) -, InvType(..)-) where--import Control.Applicative ((<$>),(<*>))--import Data.Binary (Binary, get, put)-import Data.Binary.Get (getWord32le)-import Data.Binary.Put (putWord32le)--import Network.Haskoin.Crypto.Hash ---- | Data type identifying the type of an inventory vector. -data InvType - = InvError -- ^ Error. Data containing this type can be ignored.- | InvTx -- ^ InvVector hash is related to a transaction - | InvBlock -- ^ InvVector hash is related to a block- | InvMerkleBlock -- ^ InvVector has is related to a merkle block- deriving (Eq, Show, Read)--instance Binary InvType where-- get = go =<< getWord32le- where - go x = case x of- 0 -> return InvError- 1 -> return InvTx- 2 -> return InvBlock- 3 -> return InvMerkleBlock- _ -> fail "bitcoinGet InvType: Invalid Type"-- put x = putWord32le $ case x of- InvError -> 0- InvTx -> 1- InvBlock -> 2- InvMerkleBlock -> 3---- | Invectory vectors represent hashes identifying objects such as a 'Block'--- or a 'Tx'. They are sent inside messages to notify other peers about --- new data or data they have requested.-data InvVector = - InvVector {- -- | Type of the object referenced by this inventory vector- invType :: !InvType- -- | Hash of the object referenced by this inventory vector- , invHash :: !Hash256- } deriving (Eq, Show, Read)--instance Binary InvVector where- get = InvVector <$> get <*> get- put (InvVector t h) = put t >> put h--
− Network/Haskoin/Protocol/MerkleBlock.hs
@@ -1,67 +0,0 @@-module Network.Haskoin.Protocol.MerkleBlock (MerkleBlock(..)) where--import Control.Monad (replicateM, forM_)--import Data.Word (Word8, Word32)-import Data.Bits (testBit, setBit)-import Data.Binary (Binary, get, put)-import Data.Binary.Get (getWord8, getWord32le)-import Data.Binary.Put (putWord8, putWord32le)--import Network.Haskoin.Protocol.VarInt-import Network.Haskoin.Protocol.BlockHeader-import Network.Haskoin.Crypto.Hash --data MerkleBlock = - MerkleBlock {- -- | Header information for this merkle block.- merkleHeader :: !BlockHeader- -- | Number of transactions in the block (including- -- unmatched transactions).- , merkleTotalTxns :: !Word32- -- | Hashes in depth-first order. They are used to rebuild a- -- partial merkle tree.- , mHashes :: [Hash256]- -- | Flag bits, packed per 8 in a byte. Least significant bit- -- first. Flag bits are used to rebuild a partial merkle- -- tree.- , mFlags :: [Bool]- } deriving (Eq, Show, Read)--instance Binary MerkleBlock where-- get = do- header <- get- ntx <- getWord32le- (VarInt matchLen) <- get- hashes <- replicateM (fromIntegral matchLen) get- (VarInt flagLen) <- get- ws <- replicateM (fromIntegral flagLen) getWord8- return $ MerkleBlock header ntx hashes (decodeMerkleFlags ws)-- put (MerkleBlock h ntx hashes flags) = do- put h- putWord32le ntx- put $ VarInt $ fromIntegral $ length hashes- forM_ hashes put- let ws = encodeMerkleFlags flags- put $ VarInt $ fromIntegral $ length ws- forM_ ws putWord8--decodeMerkleFlags :: [Word8] -> [Bool]-decodeMerkleFlags ws = - [ b | p <- [0..(length ws)*8-1]- , b <- [testBit (ws !! (p `div` 8)) (p `mod` 8)]- ]--encodeMerkleFlags :: [Bool] -> [Word8]-encodeMerkleFlags bs = map boolsToWord8 $ splitIn 8 bs- -splitIn :: Int -> [a] -> [[a]]-splitIn _ [] = []-splitIn c xs = take c xs : (splitIn c $ drop c xs)- -boolsToWord8 :: [Bool] -> Word8-boolsToWord8 [] = 0-boolsToWord8 xs = foldl setBit 0 (map snd $ filter fst $ zip xs [0..7])-
Network/Haskoin/Protocol/Message.hs view
@@ -1,38 +1,70 @@-module Network.Haskoin.Protocol.Message ( Message(..) ) where+module Network.Haskoin.Protocol.Message +( Message(..) +, MessageHeader(..) +) where +import Control.DeepSeq (NFData, rnf) import Control.Monad (unless)-import Control.Applicative ((<$>))+import Control.Applicative ((<$>),(<*>)) +import Data.Word (Word32) import Data.Binary (Binary, get, put) import Data.Binary.Get ( lookAhead , getByteString+ , getWord32le+ , getWord32be )-import Data.Binary.Put (putByteString)+import Data.Binary.Put + ( putByteString+ , putWord32le+ , putWord32be+ ) import qualified Data.ByteString as BS ( length , append , empty ) -import Network.Haskoin.Protocol.MessageHeader-import Network.Haskoin.Protocol.Version-import Network.Haskoin.Protocol.Addr-import Network.Haskoin.Protocol.Inv-import Network.Haskoin.Protocol.GetData-import Network.Haskoin.Protocol.NotFound-import Network.Haskoin.Protocol.GetBlocks-import Network.Haskoin.Protocol.GetHeaders-import Network.Haskoin.Protocol.Tx-import Network.Haskoin.Protocol.Block-import Network.Haskoin.Protocol.Headers-import Network.Haskoin.Protocol.BloomFilter-import Network.Haskoin.Protocol.Ping-import Network.Haskoin.Protocol.Alert+import Network.Haskoin.Protocol.Types import Network.Haskoin.Util import Network.Haskoin.Crypto.Hash +import Network.Haskoin.Constants +-- | Data type representing the header of a 'Message'. All messages sent between+-- nodes contain a message header.+data MessageHeader = + MessageHeader {+ -- | Network magic bytes. It is used to differentiate + -- messages meant for different bitcoin networks, such as+ -- prodnet and testnet.+ headMagic :: !Word32+ -- | Message command identifying the type of message.+ -- included in the payload.+ , headCmd :: !MessageCommand+ -- | Byte length of the payload.+ , headPayloadSize :: !Word32+ -- | Checksum of the payload. + , headChecksum :: !CheckSum32+ } deriving (Eq, Show, Read)++instance NFData MessageHeader where+ rnf (MessageHeader m c p s) = rnf m `seq` rnf c `seq` rnf p `seq` rnf s++instance Binary MessageHeader where++ get = MessageHeader <$> getWord32be+ <*> get+ <*> getWord32le+ <*> get++ put (MessageHeader m c l chk) = do+ putWord32be m+ put c+ putWord32le l+ put chk+ -- | The 'Message' type is used to identify all the valid messages that can be -- sent between bitcoin peers. Only values of type 'Message' will be accepted -- by other bitcoin peers as bitcoin protocol messages need to be correctly@@ -40,26 +72,47 @@ -- include the 'MessageHeader' with the correct checksum value automatically. -- No need to add the 'MessageHeader' separately. data Message - = MVersion Version + = MVersion !Version | MVerAck - | MAddr Addr - | MInv Inv - | MGetData GetData - | MNotFound NotFound - | MGetBlocks GetBlocks - | MGetHeaders GetHeaders - | MTx Tx - | MBlock Block - | MHeaders Headers + | MAddr !Addr + | MInv !Inv + | MGetData !GetData + | MNotFound !NotFound + | MGetBlocks !GetBlocks + | MGetHeaders !GetHeaders + | MTx !Tx + | MBlock !Block + | MMerkleBlock !MerkleBlock + | MHeaders !Headers | MGetAddr - | MFilterLoad FilterLoad- | MFilterAdd FilterAdd+ | MFilterLoad !FilterLoad+ | MFilterAdd !FilterAdd | MFilterClear- | MPing Ping - | MPong Pong - | MAlert Alert- deriving (Eq, Show)+ | MPing !Ping + | MPong !Pong + | MAlert !Alert+ | MReject Reject+ deriving (Eq, Show, Read) +instance NFData Message where+ rnf (MVersion v) = rnf v+ rnf (MAddr a) = rnf a+ rnf (MInv i) = rnf i+ rnf (MGetData d) = rnf d+ rnf (MNotFound n) = rnf n+ rnf (MGetBlocks b) = rnf b+ rnf (MGetHeaders h) = rnf h+ rnf (MTx t) = rnf t+ rnf (MBlock b) = rnf b+ rnf (MMerkleBlock b) = rnf b+ rnf (MHeaders h) = rnf h+ rnf (MFilterLoad f) = rnf f+ rnf (MFilterAdd f) = rnf f+ rnf (MPing p) = rnf p+ rnf (MPong p) = rnf p+ rnf (MAlert a) = rnf a+ rnf x = x `seq` ()+ instance Binary Message where get = do@@ -80,12 +133,14 @@ MCGetHeaders -> MGetHeaders <$> get MCTx -> MTx <$> get MCBlock -> MBlock <$> get+ MCMerkleBlock -> MMerkleBlock <$> get MCHeaders -> MHeaders <$> get MCFilterLoad -> MFilterLoad <$> get MCFilterAdd -> MFilterAdd <$> get MCPing -> MPing <$> get MCPong -> MPong <$> get MCAlert -> MAlert <$> get+ MCReject -> MReject <$> get _ -> fail $ "get: Invalid command " ++ (show cmd) else case cmd of MCGetAddr -> return MGetAddr @@ -95,24 +150,26 @@ put msg = do let (cmd, payload) = case msg of- (MVersion m) -> (MCVersion, encode' m)- (MVerAck) -> (MCVerAck, BS.empty)- (MAddr m) -> (MCAddr, encode' m)- (MInv m) -> (MCInv, encode' m)- (MGetData m) -> (MCGetData, encode' m)- (MNotFound m) -> (MCNotFound, encode' m)- (MGetBlocks m) -> (MCGetBlocks, encode' m)- (MGetHeaders m) -> (MCGetHeaders, encode' m)- (MTx m) -> (MCTx, encode' m)- (MBlock m) -> (MCBlock, encode' m)- (MHeaders m) -> (MCHeaders, encode' m)- (MGetAddr) -> (MCGetAddr, BS.empty)- (MFilterLoad m) -> (MCFilterLoad, encode' m)- (MFilterAdd m) -> (MCFilterAdd, encode' m)- (MFilterClear) -> (MCFilterClear, BS.empty)- (MPing m) -> (MCPing, encode' m)- (MPong m) -> (MCPong, encode' m)- (MAlert m) -> (MCAlert, encode' m)+ MVersion m -> (MCVersion, encode' m)+ MVerAck -> (MCVerAck, BS.empty)+ MAddr m -> (MCAddr, encode' m)+ MInv m -> (MCInv, encode' m)+ MGetData m -> (MCGetData, encode' m)+ MNotFound m -> (MCNotFound, encode' m)+ MGetBlocks m -> (MCGetBlocks, encode' m)+ MGetHeaders m -> (MCGetHeaders, encode' m)+ MTx m -> (MCTx, encode' m)+ MBlock m -> (MCBlock, encode' m)+ MMerkleBlock m -> (MCMerkleBlock, encode' m)+ MHeaders m -> (MCHeaders, encode' m)+ MGetAddr -> (MCGetAddr, BS.empty)+ MFilterLoad m -> (MCFilterLoad, encode' m)+ MFilterAdd m -> (MCFilterAdd, encode' m)+ MFilterClear -> (MCFilterClear, BS.empty)+ MPing m -> (MCPing, encode' m)+ MPong m -> (MCPong, encode' m)+ MAlert m -> (MCAlert, encode' m)+ MReject m -> (MCReject, encode' m) chk = chksum32 payload len = fromIntegral $ BS.length payload header = MessageHeader networkMagic cmd len chk
− Network/Haskoin/Protocol/MessageHeader.hs
@@ -1,134 +0,0 @@-module Network.Haskoin.Protocol.MessageHeader - ( MessageHeader(..) - , MessageCommand(..)- ) where--import Control.Applicative ((<$>),(<*>))--import Data.Word (Word32)-import qualified Data.ByteString as BS - ( ByteString- , takeWhile- )-import Data.Binary (Binary, get, put)-import Data.Binary.Get - ( getWord32le- , getWord32be- , getByteString- )-import Data.Binary.Put - ( putWord32le- , putWord32be- , putByteString- )--import Network.Haskoin.Util (stringToBS, bsToString)-import Network.Haskoin.Crypto.Hash (CheckSum32)---- | A 'MessageCommand' is included in a 'MessageHeader' in order to identify--- the type of message present in the payload. This allows the message --- de-serialization code to know how to decode a particular message payload.--- Every valid 'Message' constructor has a corresponding 'MessageCommand'--- constructor.-data MessageCommand - = MCVersion - | MCVerAck - | MCAddr - | MCInv - | MCGetData - | MCNotFound - | MCGetBlocks - | MCGetHeaders - | MCTx - | MCBlock - | MCHeaders - | MCGetAddr - | MCFilterLoad- | MCFilterAdd- | MCFilterClear- | MCPing - | MCPong - | MCAlert- deriving (Eq, Show, Read)--instance Binary MessageCommand where- - get = go =<< getByteString 12- where - go bs = case unpackCommand bs of- "version" -> return MCVersion- "verack" -> return MCVerAck- "addr" -> return MCAddr- "inv" -> return MCInv- "getdata" -> return MCGetData- "notfound" -> return MCNotFound- "getblocks" -> return MCGetBlocks- "getheaders" -> return MCGetHeaders- "tx" -> return MCTx- "block" -> return MCBlock- "headers" -> return MCHeaders- "getaddr" -> return MCGetAddr- "filterload" -> return MCFilterLoad- "filteradd" -> return MCFilterAdd- "filterclear" -> return MCFilterClear- "ping" -> return MCPing- "pong" -> return MCPong- "alert" -> return MCAlert- _ -> fail "get MessageCommand : Invalid command"-- put mc = putByteString $ packCommand $ case mc of- MCVersion -> "version"- MCVerAck -> "verack"- MCAddr -> "addr"- MCInv -> "inv"- MCGetData -> "getdata"- MCNotFound -> "notfound"- MCGetBlocks -> "getblocks"- MCGetHeaders -> "getheaders"- MCTx -> "tx"- MCBlock -> "block"- MCHeaders -> "headers"- MCGetAddr -> "getaddr"- MCFilterLoad -> "filterload"- MCFilterAdd -> "filteradd"- MCFilterClear -> "filterclear"- MCPing -> "ping"- MCPong -> "pong"- MCAlert -> "alert"--packCommand :: String -> BS.ByteString-packCommand s = stringToBS $ take 12 $ s ++ repeat '\NUL'--unpackCommand :: BS.ByteString -> String-unpackCommand bs = bsToString $ BS.takeWhile (/= 0) bs---- | Data type representing the header of a 'Message'. All messages sent between--- nodes contain a message header.-data MessageHeader = - MessageHeader {- -- | Network magic bytes. It is used to differentiate - -- messages meant for different bitcoin networks, such as- -- prodnet and testnet.- headMagic :: !Word32- -- | Message command identifying the type of message.- -- included in the payload.- , headCmd :: !MessageCommand- -- | Byte length of the payload.- , headPayloadSize :: !Word32- -- | Checksum of the payload. - , headChecksum :: !CheckSum32- } deriving (Eq, Show, Read)--instance Binary MessageHeader where-- get = MessageHeader <$> getWord32be- <*> get- <*> getWord32le- <*> get-- put (MessageHeader m c l chk) = do- putWord32be m- put c- putWord32le l- put chk-
− Network/Haskoin/Protocol/NetworkAddress.hs
@@ -1,45 +0,0 @@-module Network.Haskoin.Protocol.NetworkAddress ( NetworkAddress(..) ) where--import Control.Monad (liftM2)-import Control.Applicative ((<$>),(<*>))--import Data.Word (Word16, Word64)-import Data.Binary (Binary, get, put)-import Data.Binary.Get - ( getWord16be- , getWord64le- , getWord64be- )-import Data.Binary.Put- ( putWord16be- , putWord64le- , putWord64be- )---- | Data type describing a bitcoin network address. Addresses are stored in--- IPv6. IPv4 addresses are mapped to IPv6 using IPv4 mapped IPv6 addresses:--- <http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses>. Sometimes,--- timestamps are sent together with the 'NetworkAddress' such as in the 'Addr'--- data type.-data NetworkAddress = - NetworkAddress {- -- | Bitmask of services available for this address- naServices :: !Word64- -- | IPv6 address serialized as big endian- , naAddress :: !(Word64, Word64)- -- | Port number serialized as big endian- , naPort :: !Word16- } deriving (Eq, Show, Read)--instance Binary NetworkAddress where-- get = NetworkAddress <$> getWord64le- <*> (liftM2 (,) getWord64be getWord64be)- <*> getWord16be-- put (NetworkAddress s (al,ar) p) = do- putWord64le s- putWord64be al- putWord64be ar- putWord16be p-
− Network/Haskoin/Protocol/NotFound.hs
@@ -1,30 +0,0 @@-module Network.Haskoin.Protocol.NotFound ( NotFound(..) ) where--import Control.Monad (replicateM, forM_)-import Control.Applicative ((<$>))--import Data.Binary (Binary, get, put)--import Network.Haskoin.Protocol.VarInt-import Network.Haskoin.Protocol.InvVector---- | A 'NotFound' message is returned as a response to a 'GetData' message--- whe one of the requested objects could not be retrieved. This could happen,--- for example, if a tranasaction was requested and was not available in the--- memory pool of the receiving node.-data NotFound = - NotFound {- -- | Inventory vectors related to this request- notFoundList :: ![InvVector] - } deriving (Eq, Show, Read)--instance Binary NotFound where-- get = NotFound <$> (repList =<< get)- where - repList (VarInt c) = replicateM (fromIntegral c) get-- put (NotFound xs) = do- put $ VarInt $ fromIntegral $ length xs- forM_ xs put-
− Network/Haskoin/Protocol/Ping.hs
@@ -1,37 +0,0 @@-module Network.Haskoin.Protocol.Ping-( Ping(..)-, Pong(..)-) where--import Control.Applicative ((<$>))--import Data.Word (Word64)-import Data.Binary (Binary, get, put)-import Data.Binary.Get (getWord64le)-import Data.Binary.Put (putWord64le)---- | A Ping message is sent to bitcoin peers to check if a TCP\/IP connection--- is still valid.-newtype Ping = - Ping { - -- | A random nonce used to identify the recipient of the ping- -- request once a Pong response is received. - pingNonce :: Word64 - } deriving (Eq, Show, Read)---- | A Pong message is sent as a response to a ping message.-newtype Pong = - Pong { - -- | When responding to a Ping request, the nonce from the Ping- -- is copied in the Pong response.- pongNonce :: Word64 - } deriving (Eq, Show, Read)--instance Binary Ping where- get = Ping <$> getWord64le- put (Ping n) = putWord64le n--instance Binary Pong where- get = Pong <$> getWord64le- put (Pong n) = putWord64le n-
− Network/Haskoin/Protocol/Script.hs
@@ -1,252 +0,0 @@-module Network.Haskoin.Protocol.Script -( ScriptOp(..)-, Script(..)-, PushDataType(..)-, opPushData-, getScriptOps-, putScriptOps-, decodeScriptOps-, encodeScriptOps-) where--import Control.Monad (liftM2, unless, when)-import Control.Applicative ((<$>))--import Data.Word (Word8)-import Data.Binary (Binary, get, put)-import Data.Binary.Get - ( Get- , isEmpty- , getWord8- , getWord16le- , getWord32le- , getByteString- )-import Data.Binary.Put - ( Put - , putWord8- , putWord16le- , putWord32le- , putByteString- )-import qualified Data.ByteString as BS- ( ByteString- , length- )--import Network.Haskoin.Protocol.VarInt-import Network.Haskoin.Crypto.Keys -import Network.Haskoin.Util ---- | Data type representing a transaction script. Scripts are defined as lists--- of script operators 'ScriptOp'. Scripts are used to:------ * Define the spending conditions in the output of a transaction------ * Provide the spending signatures in the input of a transaction-data Script = - Script { - -- | List of script operators defining this script- scriptOps :: [ScriptOp] - }- deriving (Eq, Show, Read)--instance Binary Script where- get = do- (VarInt len) <- get- isolate (fromIntegral len) $ Script <$> getScriptOps-- put (Script ops) = do- let bs = runPut' $ putScriptOps ops- put $ VarInt $ fromIntegral $ BS.length bs- putByteString bs---- | Deserialize a list of 'ScriptOp' inside the 'Data.Binary.Get' monad.--- This deserialization does not take into account the length of the script.-getScriptOps :: Get [ScriptOp]-getScriptOps = do- empty <- isEmpty- if empty - then return [] - else liftM2 (:) get getScriptOps---- | Serialize a list of 'ScriptOp' inside the 'Data.Binary.Put' monad.--- This serialization does not take into account the length of the script.-putScriptOps :: [ScriptOp] -> Put-putScriptOps (x:xs) = put x >> putScriptOps xs-putScriptOps _ = return ()---- | Decode a 'Script' from a ByteString by omiting the length of the script.--- This is used to produce scripthash addresses.-decodeScriptOps :: BS.ByteString -> Either String Script-decodeScriptOps bs = fromRunGet getScriptOps bs msg (return . Script)- where - msg = Left "decodeScriptOps: Could not decode scriptops"---- | Encode a 'Script' into a ByteString by omiting the length of the script.--- This is used to produce scripthash addresses.-encodeScriptOps :: Script -> BS.ByteString-encodeScriptOps = runPut' . putScriptOps . scriptOps---- | Data type representing the type of an OP_PUSHDATA opcode.-data PushDataType- = - -- | The next opcode bytes is data to be pushed onto the stack- OPCODE - -- | The next byte contains the number of bytes to be pushed onto- -- the stack- | OPDATA1 - -- | The next two bytes contains the number of bytes to be pushed onto- -- the stack- | OPDATA2- -- | The next four bytes contains the number of bytes to be pushed onto- -- the stack- | OPDATA4- deriving (Show, Read, Eq)---- | Data type representing all of the operators allowed inside a 'Script'.-data ScriptOp - -- Pushing Data- = OP_PUSHDATA BS.ByteString PushDataType - | OP_0 - | OP_1NEGATE - | OP_1 | OP_2 | OP_3 | OP_4 - | OP_5 | OP_6 | OP_7 | OP_8 - | OP_9 | OP_10 | OP_11 | OP_12 - | OP_13 | OP_14 | OP_15 | OP_16 -- -- Flow control- | OP_VERIFY -- -- Stack operations- | OP_DUP -- -- Bitwise logic- | OP_EQUAL - | OP_EQUALVERIFY -- -- Crypto- | OP_HASH160 - | OP_CHECKSIG - | OP_CHECKMULTISIG -- -- Other- | OP_PUBKEY PubKey - | OP_INVALIDOPCODE Word8- deriving (Show, Read, Eq)--instance Binary ScriptOp where-- get = go =<< (fromIntegral <$> getWord8) - where - go op - | op == 0x00 = return $ OP_0- | op <= 0x4b = do- payload <- getByteString (fromIntegral op)- return $ OP_PUSHDATA payload OPCODE- | op == 0x4c = do- len <- getWord8- payload <- getByteString (fromIntegral len)- return $ OP_PUSHDATA payload OPDATA1- | op == 0x4d = do- len <- getWord16le- payload <- getByteString (fromIntegral len)- return $ OP_PUSHDATA payload OPDATA2- | op == 0x4e = do- len <- getWord32le- payload <- getByteString (fromIntegral len)- return $ OP_PUSHDATA payload OPDATA4- | op == 0x4f = return $ OP_1NEGATE- | op == 0x51 = return $ OP_1- | op == 0x52 = return $ OP_2- | op == 0x53 = return $ OP_3- | op == 0x54 = return $ OP_4- | op == 0x55 = return $ OP_5- | op == 0x56 = return $ OP_6- | op == 0x57 = return $ OP_7- | op == 0x58 = return $ OP_8- | op == 0x59 = return $ OP_9- | op == 0x5a = return $ OP_10- | op == 0x5b = return $ OP_11- | op == 0x5c = return $ OP_12- | op == 0x5d = return $ OP_13- | op == 0x5e = return $ OP_14- | op == 0x5f = return $ OP_15- | op == 0x60 = return $ OP_16- | op == 0x69 = return $ OP_VERIFY- | op == 0x76 = return $ OP_DUP- | op == 0x87 = return $ OP_EQUAL- | op == 0x88 = return $ OP_EQUALVERIFY- | op == 0xa9 = return $ OP_HASH160- | op == 0xac = return $ OP_CHECKSIG- | op == 0xae = return $ OP_CHECKMULTISIG- | op == 0xfe = OP_PUBKEY <$> get- | otherwise = return $ OP_INVALIDOPCODE op-- put op = case op of-- (OP_PUSHDATA payload optype)-> do- let len = BS.length payload- when (len == 0) $ fail "OP_PUSHDATA: Payload size must be > 0"- case optype of- OPCODE -> do- unless (len <= 0x4b) $ fail - "OP_PUSHDATA OPCODE: Payload size too big"- putWord8 $ fromIntegral len- OPDATA1 -> do- unless (len <= 0xff) $ fail - "OP_PUSHDATA OPDATA1: Payload size too big"- putWord8 0x4c- putWord8 $ fromIntegral len- OPDATA2 -> do- unless (len <= 0xffff) $ fail - "OP_PUSHDATA OPDATA2: Payload size too big"- putWord8 0x4d- putWord16le $ fromIntegral len- OPDATA4 -> do- unless (len <= 0xffffffff) $ fail - "OP_PUSHDATA OPDATA4: Payload size too big"- putWord8 0x4e- putWord32le $ fromIntegral len- putByteString payload-- OP_0 -> putWord8 0x00- OP_1NEGATE -> putWord8 0x4f- OP_1 -> putWord8 0x51- OP_2 -> putWord8 0x52- OP_3 -> putWord8 0x53- OP_4 -> putWord8 0x54- OP_5 -> putWord8 0x55- OP_6 -> putWord8 0x56- OP_7 -> putWord8 0x57- OP_8 -> putWord8 0x58- OP_9 -> putWord8 0x59- OP_10 -> putWord8 0x5a- OP_11 -> putWord8 0x5b- OP_12 -> putWord8 0x5c- OP_13 -> putWord8 0x5d- OP_14 -> putWord8 0x5e- OP_15 -> putWord8 0x5f- OP_16 -> putWord8 0x60- OP_VERIFY -> putWord8 0x69- OP_DUP -> putWord8 0x76- OP_EQUAL -> putWord8 0x87- OP_EQUALVERIFY -> putWord8 0x88- OP_HASH160 -> putWord8 0xa9- OP_CHECKSIG -> putWord8 0xac- OP_CHECKMULTISIG -> putWord8 0xae- (OP_PUBKEY pk) -> putWord8 0xfe >> put pk- (OP_INVALIDOPCODE _) -> putWord8 0xff---- | Optimally encode data using one of the 4 types of data pushing opcodes-opPushData :: BS.ByteString -> ScriptOp-opPushData bs- | len <= 0 = error "opPushData: data length must be > 0"- | len <= 0x4b = OP_PUSHDATA bs OPCODE- | len <= 0xff = OP_PUSHDATA bs OPDATA1- | len <= 0xffff = OP_PUSHDATA bs OPDATA2- | len <= 0xffffffff = OP_PUSHDATA bs OPDATA4- | otherwise = error "opPushData: payload size too big"- where- len = BS.length bs-
− Network/Haskoin/Protocol/Tx.hs
@@ -1,194 +0,0 @@-module Network.Haskoin.Protocol.Tx -( Tx(..) -, TxIn(..)-, TxOut(..)-, OutPoint(..)-, CoinbaseTx(..)-, txid-, cbid-, encodeTxid-, decodeTxid-) where--import Control.Monad (replicateM, forM_, liftM2, unless)-import Control.Applicative ((<$>),(<*>))--import Data.Word (Word32, Word64)-import Data.Binary (Binary, get, put)-import Data.Binary.Get - ( getWord32le- , getWord64le- , getByteString- )-import Data.Binary.Put - ( putWord32le- , putWord64le- , putByteString- )-import qualified Data.ByteString as BS - ( ByteString- , length- , reverse- )--import Network.Haskoin.Protocol.VarInt-import Network.Haskoin.Protocol.Script-import Network.Haskoin.Crypto.Hash -import Network.Haskoin.Util ---- | Data type representing a bitcoin transaction-data Tx = - Tx { - -- | Transaction data format version- txVersion :: !Word32- -- | List of transaction inputs- , txIn :: ![TxIn]- -- | List of transaction outputs- , txOut :: ![TxOut]- -- | The block number of timestamp at which this transaction is locked- , txLockTime :: !Word32- } deriving (Eq, Show, Read)--instance Binary Tx where-- get = Tx <$> getWord32le- <*> (replicateList =<< get)- <*> (replicateList =<< get)- <*> getWord32le- where - replicateList (VarInt c) = replicateM (fromIntegral c) get-- put (Tx v is os l) = do- putWord32le v- put $ VarInt $ fromIntegral $ length is- forM_ is put- put $ VarInt $ fromIntegral $ length os- forM_ os put- putWord32le l---- | Data type representing the coinbase transaction of a 'Block'. Coinbase--- transactions are special types of transactions which are created by miners--- when they find a new block. Coinbase transactions have no inputs. They have--- outputs sending the newly generated bitcoins together with all the block's--- fees to a bitcoin address (usually the miners address). Data can be embedded--- in a Coinbase transaction which can be chosen by the miner of a block. This--- data also typically contains some randomness which is used, together with--- the nonce, to find a partial hash collision on the block's hash.-data CoinbaseTx = - CoinbaseTx { - -- | Transaction data format version.- cbVersion :: !Word32- -- | Previous outpoint. This is ignored for- -- coinbase transactions but preserved for computing- -- the correct txid.- , cbPrevOutput :: !OutPoint- -- | Data embedded inside the coinbase transaction.- , cbData :: !BS.ByteString- -- | Transaction sequence number. This is ignored for- -- coinbase transactions but preserved for computing- -- the correct txid.- , cbInSequence :: !Word32- -- | List of transaction outputs.- , cbOut :: ![TxOut]- -- | The block number of timestamp at which this - -- transaction is locked.- , cbLockTime :: !Word32- } deriving (Eq, Show, Read)--instance Binary CoinbaseTx where-- get = do- v <- getWord32le- (VarInt len) <- get- unless (len == 1) $ fail "CoinbaseTx get: Input size is not 1"- op <- get- (VarInt cbLen) <- get- cb <- getByteString (fromIntegral cbLen)- sq <- getWord32le- (VarInt oLen) <- get- os <- replicateM (fromIntegral oLen) get- lt <- getWord32le- return $ CoinbaseTx v op cb sq os lt-- put (CoinbaseTx v op cb sq os lt) = do- putWord32le v- put $ VarInt 1- put op- put $ VarInt $ fromIntegral $ BS.length cb- putByteString cb - putWord32le sq- put $ VarInt $ fromIntegral $ length os- forM_ os put- putWord32le lt---- | Data type representing a transaction input.-data TxIn = - TxIn { - -- | Reference the previous transaction output (hash + position)- prevOutput :: !OutPoint- -- | Script providing the requirements of the previous transaction- -- output to spend those coins.- , scriptInput :: !Script- -- | Transaction version as defined by the sender of the- -- transaction. The intended use is for replacing transactions with- -- new information before the transaction is included in a block.- , txInSequence :: !Word32- } deriving (Eq, Show, Read)--instance Binary TxIn where- get = TxIn <$> get <*> get <*> getWord32le- put (TxIn o s q) = put o >> put s >> putWord32le q---- | Data type representing a transaction output.-data TxOut = - TxOut { - -- | Transaction output value.- outValue :: !Word64- -- | Script specifying the conditions to spend this output.- , scriptOutput :: !Script- } deriving (Eq, Show, Read)--instance Binary TxOut where- get = do- val <- getWord64le- unless (val <= 2100000000000000) $ fail $- "Invalid TxOut value: " ++ (show val)- TxOut val <$> get- put (TxOut o s) = putWord64le o >> put s---- | The OutPoint is used inside a transaction input to reference the previous--- transaction output that it is spending.-data OutPoint = - OutPoint { - -- | The hash of the referenced transaction.- outPointHash :: !Hash256- -- | The position of the specific output in the transaction.- -- The first output position is 0.- , outPointIndex :: !Word32- } deriving (Read, Show, Eq)--instance Binary OutPoint where- get = do- (h,i) <- liftM2 (,) get getWord32le- return $ OutPoint h i- put (OutPoint h i) = put h >> putWord32le i---- | Computes the hash of a transaction.-txid :: Tx -> Hash256-txid = doubleHash256 . encode' ---- | Computes the hash of a coinbase transaction.-cbid :: CoinbaseTx -> Hash256-cbid = doubleHash256 . encode' ---- | Encodes a transaction hash as little endian in HEX format.--- This is mostly used for displaying transaction ids. Internally, these ids--- are handled as big endian but are transformed to little endian when--- displaying them.-encodeTxid :: Hash256 -> String-encodeTxid = bsToHex . BS.reverse . encode' ---- | Decodes a little endian transaction hash in HEX format. -decodeTxid :: String -> Maybe Hash256-decodeTxid = (decodeToMaybe . BS.reverse =<<) . hexToBS-
+ Network/Haskoin/Protocol/Types.hs view
@@ -0,0 +1,1089 @@+module Network.Haskoin.Protocol.Types +( Addr(..)+, NetworkAddressTime +, Alert(..)+, Block(..)+, BlockHeader(..) +, BloomFlags(..)+, BloomFilter(..)+, FilterLoad(..)+, FilterAdd(..)+, GetBlocks(..) +, BlockLocator+, GetData(..)+, GetHeaders(..)+, Headers(..)+, BlockHeaderCount+, Inv(..)+, InvVector(..) +, InvType(..)+, MerkleBlock(..)+, NetworkAddress(..)+, NotFound(..)+, Ping(..)+, Pong(..)+, Reject(..)+, RejectCode(..)+, reject+, Tx(..) +, TxIn(..)+, TxOut(..)+, OutPoint(..)+, CoinbaseTx(..)+, VarInt(..)+, VarString(..)+, Version(..)+, MessageCommand(..)+) where++import Control.DeepSeq (NFData, rnf)+import Control.Monad (liftM2, replicateM, forM_, unless)+import Control.Applicative ((<$>),(<*>))++import Data.Aeson (Value(String), FromJSON, ToJSON, parseJSON, toJSON, withText)+import Data.Bits (testBit, setBit)+import Data.Word (Word8, Word16, Word32, Word64)+import qualified Data.Text as T+import Data.Binary (Binary, get, put)+import Data.Binary.Get + ( Get+ , getWord8+ , getWord16le+ , getWord16be+ , getWord32le+ , getWord64le+ , getWord64be+ , getByteString+ , isEmpty+ )+import Data.Binary.Put + ( Put+ , putWord8+ , putWord16le+ , putWord16be+ , putWord32le+ , putWord64le+ , putWord64be+ , putByteString+ )+import qualified Data.Foldable as F (toList)+import qualified Data.Sequence as S (Seq, fromList, length)+import qualified Data.ByteString as BS + ( ByteString+ , length+ , takeWhile+ )++import Network.Haskoin.Util +import Network.Haskoin.Crypto.BigWord++-- | Network address with a timestamp+type NetworkAddressTime = (Word32, NetworkAddress)++-- | Provides information on known nodes in the bitcoin network. An 'Addr'+-- type is sent inside a 'Message' as a response to a 'GetAddr' message.+data Addr = + Addr { + -- List of addresses of other nodes on the network with timestamps.+ addrList :: ![NetworkAddressTime] + } + deriving (Eq, Show, Read)++instance NFData Addr where+ rnf (Addr as) = rnf as++instance Binary Addr where++ get = Addr <$> (repList =<< get)+ where + repList (VarInt c) = replicateM (fromIntegral c) action+ action = liftM2 (,) getWord32le get ++ put (Addr xs) = do+ put $ VarInt $ fromIntegral $ length xs+ forM_ xs $ \(a,b) -> (putWord32le a) >> (put b)++-- | Data type describing signed messages that can be sent between bitcoin+-- nodes to display important notifications to end users about the health of+-- the network.+data Alert = + Alert {+ -- | Alert payload. + alertPayload :: !VarString+ -- | ECDSA signature of the payload+ , alertSignature :: !VarString+ } deriving (Eq, Show, Read)++instance NFData Alert where+ rnf (Alert p s) = rnf p `seq` rnf s++instance Binary Alert where+ get = Alert <$> get <*> get+ put (Alert p s) = put p >> put s++-- | Data type describing a block in the bitcoin protocol. Blocks are sent in+-- response to 'GetData' messages that are requesting information from a+-- block hash.+data Block = + Block {+ -- | Header information for this block.+ blockHeader :: !BlockHeader+ -- | Coinbase transaction of this block.+ , blockCoinbaseTx :: !CoinbaseTx+ -- | List of transactions pertaining to this block.+ , blockTxns :: ![Tx]+ } deriving (Eq, Show, Read)++instance NFData Block where+ rnf (Block h c ts) = rnf h `seq` rnf c `seq` rnf ts++instance Binary Block where++ get = do+ header <- get+ (VarInt c) <- get+ cb <- get+ txs <- replicateM (fromIntegral (c-1)) get+ return $ Block header cb txs++ put (Block h cb txs) = do+ put h+ put $ VarInt $ fromIntegral $ (length txs) + 1+ put cb+ forM_ txs put++-- | Data type recording information on a 'Block'. The hash of a block is+-- defined as the hash of this data structure. The block mining process+-- involves finding a partial hash collision by varying the nonce in the+-- 'BlockHeader' and/or additional randomness in the 'CoinbaseTx' of this+-- 'Block'. Variations in the 'CoinbaseTx' will result in different merkle +-- roots in the 'BlockHeader'.+data BlockHeader = + BlockHeader {+ -- | Block version information, based on the version of the+ -- software creating this block.+ blockVersion :: !Word32+ -- | Hash of the previous block (parent) referenced by this+ -- block.+ , prevBlock :: !BlockHash+ -- | Root of the merkle tree of all transactions pertaining+ -- to this block.+ , merkleRoot :: !Word256+ -- | Unix timestamp recording when this block was created+ , blockTimestamp :: !Word32+ -- | The difficulty target being used for this block+ , blockBits :: !Word32+ -- | A random nonce used to generate this block. Additional+ -- randomness is included in the coinbase transaction of+ -- this block.+ , bhNonce :: !Word32+ } deriving (Eq, Show, Read)++instance NFData BlockHeader where+ rnf (BlockHeader v p m t b n) =+ rnf v `seq` rnf p `seq` rnf m `seq` rnf t `seq` rnf b `seq` rnf n++instance Binary BlockHeader where++ get = BlockHeader <$> getWord32le+ <*> get+ <*> get+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le++ put (BlockHeader v p m bt bb n) = do+ putWord32le v+ put p+ put m+ putWord32le bt+ putWord32le bb+ putWord32le n ++-- | The bloom flags are used to tell the remote peer how to auto-update+-- the provided bloom filter. +data BloomFlags+ = BloomUpdateNone -- ^ Never update+ | BloomUpdateAll -- ^ Auto-update on all outputs+ | BloomUpdateP2PubKeyOnly + -- ^ Only auto-update on outputs that are pay-to-pubkey or pay-to-multisig.+ -- This is the default setting.+ deriving (Eq, Show, Read)++instance NFData BloomFlags++instance Binary BloomFlags where+ get = go =<< getWord8+ where+ go 0 = return BloomUpdateNone+ go 1 = return BloomUpdateAll+ go 2 = return BloomUpdateP2PubKeyOnly+ go _ = fail "BloomFlags get: Invalid bloom flag"++ put f = putWord8 $ case f of+ BloomUpdateNone -> 0+ BloomUpdateAll -> 1+ BloomUpdateP2PubKeyOnly -> 2+ +-- | A bloom filter is a probabilistic data structure that SPV clients send to+-- other peers to filter the set of transactions received from them. Bloom+-- filters are probabilistic and have a false positive rate. Some transactions+-- that pass the filter may not be relevant to the receiving peer. By+-- controlling the false positive rate, SPV nodes can trade off bandwidth+-- versus privacy.+data BloomFilter = BloomFilter+ { bloomData :: !(S.Seq Word8)+ -- ^ Bloom filter data+ , bloomHashFuncs :: !Word32+ -- ^ Number of hash functions for this filter+ , bloomTweak :: !Word32+ -- ^ Hash function random nonce+ , bloomFlags :: !BloomFlags+ -- ^ Bloom filter auto-update flags+ }+ deriving (Eq, Show, Read)++instance NFData BloomFilter where+ rnf (BloomFilter d h t g) =+ rnf d `seq` rnf h `seq` rnf t `seq` rnf g++instance Binary BloomFilter where++ get = BloomFilter <$> (S.fromList <$> (readDat =<< get))+ <*> getWord32le <*> getWord32le+ <*> get+ where+ readDat (VarInt len) = replicateM (fromIntegral len) getWord8 ++ put (BloomFilter dat hashFuncs tweak flags) = do+ put $ VarInt $ fromIntegral $ S.length dat+ forM_ (F.toList dat) putWord8+ putWord32le hashFuncs+ putWord32le tweak+ put flags++-- | Set a new bloom filter on the peer connection.+newtype FilterLoad = FilterLoad { getBloomFilter :: BloomFilter }+ deriving (Eq, Show, Read)++instance NFData FilterLoad where+ rnf (FilterLoad f) = rnf f++instance Binary FilterLoad where+ get = FilterLoad <$> get+ put (FilterLoad f) = put f++-- | Add the given data element to the connections current filter without+-- requiring a completely new one to be set.+newtype FilterAdd = FilterAdd { getFilterData :: BS.ByteString }+ deriving (Eq, Show, Read)++instance NFData FilterAdd where+ rnf (FilterAdd f) = rnf f++instance Binary FilterAdd where+ get = do+ (VarInt len) <- get+ dat <- getByteString $ fromIntegral len+ return $ FilterAdd dat++ put (FilterAdd bs) = do+ put $ VarInt $ fromIntegral $ BS.length bs+ putByteString bs++type BlockLocator = [BlockHash]++-- | Data type representing a GetBlocks message request. It is used in the+-- bitcoin protocol to retrieve blocks from a peer by providing it a+-- 'BlockLocator' object. The 'BlockLocator' is a sparse list of block hashes+-- from the caller node with the purpose of informing the receiving node+-- about the state of the caller's blockchain. The receiver node will detect+-- a wrong branch in the caller's main chain and send the caller appropriate +-- 'Blocks'. The response to a 'GetBlocks' message is an 'Inv' message+-- containing the list of block hashes pertaining to the request. +data GetBlocks = + GetBlocks {+ -- | The protocol version+ getBlocksVersion :: !Word32+ -- | Block locator object. It is a list of block hashes from the+ -- most recent block back to the genesis block. The list is+ -- dense at first and sparse towards the end.+ , getBlocksLocator :: !BlockLocator+ -- | Hash of the last desired block. If set to zero, the+ -- maximum number of block hashes is returned (500).+ , getBlocksHashStop :: !BlockHash+ } deriving (Eq, Show, Read)++instance NFData GetBlocks where+ rnf (GetBlocks v l h) = rnf v `seq` rnf l `seq` rnf h++instance Binary GetBlocks where++ get = GetBlocks <$> getWord32le+ <*> (repList =<< get)+ <*> get+ where + repList (VarInt c) = replicateM (fromIntegral c) get++ put (GetBlocks v xs h) = do+ putWord32le v+ put $ VarInt $ fromIntegral $ length xs+ forM_ xs put+ put h++-- | The 'GetData' type is used to retrieve information on a specific object+-- ('Block' or 'Tx') identified by the objects hash. The payload of a 'GetData'+-- request is a list of 'InvVector' which represent all the hashes for which a+-- node wants to request information. The response to a 'GetBlock' message+-- wille be either a 'Block' or a 'Tx' message depending on the type of the+-- object referenced by the hash. Usually, 'GetData' messages are sent after a+-- node receives an 'Inv' message to obtain information on unknown object+-- hashes. +data GetData = + GetData {+ -- | List of object hashes + getDataList :: ![InvVector] + } deriving (Eq, Show, Read)++instance NFData GetData where+ rnf (GetData l) = rnf l++instance Binary GetData where++ get = GetData <$> (repList =<< get)+ where + repList (VarInt c) = replicateM (fromIntegral c) get++ put (GetData xs) = do+ put $ VarInt $ fromIntegral $ length xs+ forM_ xs put++-- | Similar to the 'GetBlocks' message type but for retrieving block headers+-- only. The response to a 'GetHeaders' request is a 'Headers' message+-- containing a list of block headers pertaining to the request. A maximum of+-- 2000 block headers can be returned. 'GetHeaders' is used by thin (SPV)+-- clients to exclude block contents when synchronizing the blockchain.+data GetHeaders = + GetHeaders {+ -- | The protocol version+ getHeadersVersion :: !Word32+ -- | Block locator object. It is a list of block hashes from+ -- the most recent block back to the Genesis block. The list+ -- is dense at first and sparse towards the end.+ , getHeadersBL :: !BlockLocator+ -- | Hash of the last desired block header. When set to zero,+ -- the maximum number of block headers is returned (2000)+ , getHeadersHashStop :: !BlockHash+ } deriving (Eq, Show, Read)++instance NFData GetHeaders where+ rnf (GetHeaders v l h) = rnf v `seq` rnf l `seq` rnf h++instance Binary GetHeaders where++ get = GetHeaders <$> getWord32le+ <*> (repList =<< get)+ <*> get+ where + repList (VarInt c) = replicateM (fromIntegral c) get++ put (GetHeaders v xs h) = do+ putWord32le v+ put $ VarInt $ fromIntegral $ length xs+ forM_ xs put+ put h++-- | 'BlockHeader' type with a transaction count as 'VarInt'+type BlockHeaderCount = (BlockHeader, VarInt)++-- | The 'Headers' type is used to return a list of block headers in+-- response to a 'GetHeaders' message.+data Headers = + Headers { + -- | List of block headers with respective transaction counts+ headersList :: ![BlockHeaderCount] + } + deriving (Eq, Show, Read)++instance NFData Headers where+ rnf (Headers l) = rnf l++instance Binary Headers where++ get = Headers <$> (repList =<< get)+ where + repList (VarInt c) = replicateM (fromIntegral c) action+ action = liftM2 (,) get get++ put (Headers xs) = do+ put $ VarInt $ fromIntegral $ length xs+ forM_ xs $ \(a,b) -> put a >> put b++-- | 'Inv' messages are used by nodes to advertise their knowledge of new+-- objects by publishing a list of hashes. 'Inv' messages can be sent+-- unsolicited or in response to a 'GetBlocks' message.+data Inv = + Inv { + -- | Inventory vectors+ invList :: ![InvVector] + } deriving (Eq, Show, Read)++instance NFData Inv where+ rnf (Inv l) = rnf l++instance Binary Inv where++ get = Inv <$> (repList =<< get)+ where + repList (VarInt c) = replicateM (fromIntegral c) get++ put (Inv xs) = do+ put $ VarInt $ fromIntegral $ length xs+ forM_ xs put++-- | Data type identifying the type of an inventory vector. +data InvType + = InvError -- ^ Error. Data containing this type can be ignored.+ | InvTx -- ^ InvVector hash is related to a transaction + | InvBlock -- ^ InvVector hash is related to a block+ | InvMerkleBlock -- ^ InvVector has is related to a merkle block+ deriving (Eq, Show, Read)++instance NFData InvType++instance Binary InvType where++ get = go =<< getWord32le+ where + go x = case x of+ 0 -> return InvError+ 1 -> return InvTx+ 2 -> return InvBlock+ 3 -> return InvMerkleBlock+ _ -> fail "bitcoinGet InvType: Invalid Type"++ put x = putWord32le $ case x of+ InvError -> 0+ InvTx -> 1+ InvBlock -> 2+ InvMerkleBlock -> 3++-- | Invectory vectors represent hashes identifying objects such as a 'Block'+-- or a 'Tx'. They are sent inside messages to notify other peers about +-- new data or data they have requested.+data InvVector = + InvVector {+ -- | Type of the object referenced by this inventory vector+ invType :: !InvType+ -- | Hash of the object referenced by this inventory vector+ , invHash :: !Word256+ } deriving (Eq, Show, Read)++instance NFData InvVector where+ rnf (InvVector t h) = rnf t `seq` rnf h++instance Binary InvVector where+ get = InvVector <$> get <*> get+ put (InvVector t h) = put t >> put h++data MerkleBlock = + MerkleBlock {+ -- | Header information for this merkle block.+ merkleHeader :: !BlockHeader+ -- | Number of transactions in the block (including+ -- unmatched transactions).+ , merkleTotalTxns :: !Word32+ -- | Hashes in depth-first order. They are used to rebuild a+ -- partial merkle tree.+ , mHashes :: ![Word256]+ -- | Flag bits, packed per 8 in a byte. Least significant bit+ -- first. Flag bits are used to rebuild a partial merkle+ -- tree.+ , mFlags :: ![Bool]+ } deriving (Eq, Show, Read)++instance NFData MerkleBlock where+ rnf (MerkleBlock m t h f) = rnf m `seq` rnf t `seq` rnf h `seq` rnf f++instance Binary MerkleBlock where++ get = do+ header <- get+ ntx <- getWord32le+ (VarInt matchLen) <- get+ hashes <- replicateM (fromIntegral matchLen) get+ (VarInt flagLen) <- get+ ws <- replicateM (fromIntegral flagLen) getWord8+ return $ MerkleBlock header ntx hashes (decodeMerkleFlags ws)++ put (MerkleBlock h ntx hashes flags) = do+ put h+ putWord32le ntx+ put $ VarInt $ fromIntegral $ length hashes+ forM_ hashes put+ let ws = encodeMerkleFlags flags+ put $ VarInt $ fromIntegral $ length ws+ forM_ ws putWord8++decodeMerkleFlags :: [Word8] -> [Bool]+decodeMerkleFlags ws = + [ b | p <- [0..(length ws)*8-1]+ , b <- [testBit (ws !! (p `div` 8)) (p `mod` 8)]+ ]++encodeMerkleFlags :: [Bool] -> [Word8]+encodeMerkleFlags bs = map boolsToWord8 $ splitIn 8 bs+ +splitIn :: Int -> [a] -> [[a]]+splitIn _ [] = []+splitIn c xs = take c xs : (splitIn c $ drop c xs)+ +boolsToWord8 :: [Bool] -> Word8+boolsToWord8 [] = 0+boolsToWord8 xs = foldl setBit 0 (map snd $ filter fst $ zip xs [0..7])++-- | Data type describing a bitcoin network address. Addresses are stored in+-- IPv6. IPv4 addresses are mapped to IPv6 using IPv4 mapped IPv6 addresses:+-- <http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses>. Sometimes,+-- timestamps are sent together with the 'NetworkAddress' such as in the 'Addr'+-- data type.+data NetworkAddress = + NetworkAddress {+ -- | Bitmask of services available for this address+ naServices :: !Word64+ -- | IPv6 address serialized as big endian+ , naAddress :: !(Word64, Word64)+ -- | Port number serialized as big endian+ , naPort :: !Word16+ } deriving (Eq, Show, Read)++instance NFData NetworkAddress where+ rnf (NetworkAddress s a p) = rnf s `seq` rnf a `seq` rnf p++instance Binary NetworkAddress where++ get = NetworkAddress <$> getWord64le+ <*> (liftM2 (,) getWord64be getWord64be)+ <*> getWord16be++ put (NetworkAddress s (al,ar) p) = do+ putWord64le s+ putWord64be al+ putWord64be ar+ putWord16be p++-- | A 'NotFound' message is returned as a response to a 'GetData' message+-- whe one of the requested objects could not be retrieved. This could happen,+-- for example, if a tranasaction was requested and was not available in the+-- memory pool of the receiving node.+data NotFound = + NotFound {+ -- | Inventory vectors related to this request+ notFoundList :: ![InvVector] + } deriving (Eq, Show, Read)++instance NFData NotFound where+ rnf (NotFound l) = rnf l++instance Binary NotFound where++ get = NotFound <$> (repList =<< get)+ where + repList (VarInt c) = replicateM (fromIntegral c) get++ put (NotFound xs) = do+ put $ VarInt $ fromIntegral $ length xs+ forM_ xs put++-- | A Ping message is sent to bitcoin peers to check if a TCP\/IP connection+-- is still valid.+newtype Ping = + Ping { + -- | A random nonce used to identify the recipient of the ping+ -- request once a Pong response is received. + pingNonce :: Word64 + } deriving (Eq, Show, Read)++instance NFData Ping where+ rnf (Ping n) = rnf n++-- | A Pong message is sent as a response to a ping message.+newtype Pong = + Pong { + -- | When responding to a Ping request, the nonce from the Ping+ -- is copied in the Pong response.+ pongNonce :: Word64 + } deriving (Eq, Show, Read)++instance NFData Pong where+ rnf (Pong n) = rnf n++instance Binary Ping where+ get = Ping <$> getWord64le+ put (Ping n) = putWord64le n++instance Binary Pong where+ get = Pong <$> getWord64le+ put (Pong n) = putWord64le n++-- | The reject message is sent when messages are rejected by a peer.+data Reject =+ Reject { + -- | Type of message rejected+ rejectMessage :: !MessageCommand+ -- | Code related to the rejected message+ , rejectCode :: !RejectCode+ -- | Text version of rejected reason+ , rejectReason :: !VarString+ } deriving (Eq, Show, Read)+++data RejectCode + = RejectMalformed+ | RejectInvalid+ | RejectObsolete+ | RejectDuplicate+ | RejectNonStandard+ | RejectDust+ | RejectInsufficientFee+ | RejectCheckpoint+ deriving (Eq, Show, Read)++instance Binary RejectCode where++ get = getWord8 >>= \code -> case code of+ 0x01 -> return RejectMalformed+ 0x10 -> return RejectInvalid+ 0x11 -> return RejectObsolete+ 0x12 -> return RejectDuplicate+ 0x40 -> return RejectNonStandard+ 0x41 -> return RejectDust+ 0x42 -> return RejectInsufficientFee+ 0x43 -> return RejectCheckpoint+ _ -> fail $ unwords+ [ "Reject get: Invalid code"+ , show code+ ]++ put code = putWord8 $ case code of+ RejectMalformed -> 0x01+ RejectInvalid -> 0x10+ RejectObsolete -> 0x11+ RejectDuplicate -> 0x12+ RejectNonStandard -> 0x40+ RejectDust -> 0x41+ RejectInsufficientFee -> 0x42+ RejectCheckpoint -> 0x43++-- | Convenience function to build a Reject message+reject :: MessageCommand -> RejectCode -> String -> Reject+reject cmd code reason = Reject cmd code (VarString $ stringToBS reason)++instance Binary Reject where++ get = get >>= \(VarString bs) -> case stringToCommand $ bsToString bs of+ Just cmd -> Reject cmd <$> get <*> get+ _ -> fail $ unwords $+ [ "Reason get: Invalid message command"+ , bsToString bs+ ]++ put (Reject cmd code reason) = do+ put $ VarString $ stringToBS $ commandToString cmd+ put code+ put reason++-- | Data type representing a bitcoin transaction+data Tx = + Tx { + -- | Transaction data format version+ txVersion :: !Word32+ -- | List of transaction inputs+ , txIn :: ![TxIn]+ -- | List of transaction outputs+ , txOut :: ![TxOut]+ -- | The block number of timestamp at which this transaction is locked+ , txLockTime :: !Word32+ } deriving (Eq, Show, Read)++instance NFData Tx where+ rnf (Tx v i o l) = rnf v `seq` rnf i `seq` rnf o `seq` rnf l++instance Binary Tx where++ get = Tx <$> getWord32le+ <*> (replicateList =<< get)+ <*> (replicateList =<< get)+ <*> getWord32le+ where + replicateList (VarInt c) = replicateM (fromIntegral c) get++ put (Tx v is os l) = do+ putWord32le v+ put $ VarInt $ fromIntegral $ length is+ forM_ is put+ put $ VarInt $ fromIntegral $ length os+ forM_ os put+ putWord32le l++instance FromJSON Tx where+ parseJSON = withText "transaction" $ \t -> either fail return $+ maybeToEither "tx not hex" (hexToBS $ T.unpack t) >>= decodeToEither++instance ToJSON Tx where+ toJSON = String . T.pack . bsToHex . encode'++-- | Data type representing the coinbase transaction of a 'Block'. Coinbase+-- transactions are special types of transactions which are created by miners+-- when they find a new block. Coinbase transactions have no inputs. They have+-- outputs sending the newly generated bitcoins together with all the block's+-- fees to a bitcoin address (usually the miners address). Data can be embedded+-- in a Coinbase transaction which can be chosen by the miner of a block. This+-- data also typically contains some randomness which is used, together with+-- the nonce, to find a partial hash collision on the block's hash.+data CoinbaseTx = + CoinbaseTx { + -- | Transaction data format version.+ cbVersion :: !Word32+ -- | Previous outpoint. This is ignored for+ -- coinbase transactions but preserved for computing+ -- the correct txid.+ , cbPrevOutput :: !OutPoint+ -- | Data embedded inside the coinbase transaction.+ , cbData :: !BS.ByteString+ -- | Transaction sequence number. This is ignored for+ -- coinbase transactions but preserved for computing+ -- the correct txid.+ , cbInSequence :: !Word32+ -- | List of transaction outputs.+ , cbOut :: ![TxOut]+ -- | The block number of timestamp at which this + -- transaction is locked.+ , cbLockTime :: !Word32+ } deriving (Eq, Show, Read)++instance NFData CoinbaseTx where+ rnf (CoinbaseTx v p d i o l) =+ rnf v `seq` rnf p `seq` rnf d `seq` rnf i `seq` rnf o `seq` rnf l++instance Binary CoinbaseTx where++ get = do+ v <- getWord32le+ (VarInt len) <- get+ unless (len == 1) $ fail "CoinbaseTx get: Input size is not 1"+ op <- get+ (VarInt cbLen) <- get+ cb <- getByteString (fromIntegral cbLen)+ sq <- getWord32le+ (VarInt oLen) <- get+ os <- replicateM (fromIntegral oLen) get+ lt <- getWord32le+ return $ CoinbaseTx v op cb sq os lt++ put (CoinbaseTx v op cb sq os lt) = do+ putWord32le v+ put $ VarInt 1+ put op+ put $ VarInt $ fromIntegral $ BS.length cb+ putByteString cb + putWord32le sq+ put $ VarInt $ fromIntegral $ length os+ forM_ os put+ putWord32le lt++-- | Data type representing a transaction input.+data TxIn = + TxIn { + -- | Reference the previous transaction output (hash + position)+ prevOutput :: !OutPoint+ -- | Script providing the requirements of the previous transaction+ -- output to spend those coins.+ , scriptInput :: !BS.ByteString+ -- | Transaction version as defined by the sender of the+ -- transaction. The intended use is for replacing transactions with+ -- new information before the transaction is included in a block.+ , txInSequence :: !Word32+ } deriving (Eq, Show, Read)++instance NFData TxIn where+ rnf (TxIn p i s) = rnf p `seq` rnf i `seq` rnf s++instance Binary TxIn where++ get = + TxIn <$> get <*> (readBS =<< get) <*> getWord32le+ where+ readBS (VarInt len) = getByteString $ fromIntegral len++ put (TxIn o s q) = do+ put o + put $ VarInt $ fromIntegral $ BS.length s+ putByteString s+ putWord32le q++-- | Data type representing a transaction output.+data TxOut = + TxOut { + -- | Transaction output value.+ outValue :: !Word64+ -- | Script specifying the conditions to spend this output.+ , scriptOutput :: !BS.ByteString+ } deriving (Eq, Show, Read)++instance NFData TxOut where+ rnf (TxOut v o) = rnf v `seq` rnf o++instance Binary TxOut where++ get = do+ val <- getWord64le+ unless (val <= 2100000000000000) $ fail $+ "Invalid TxOut value: " ++ (show val)+ (VarInt len) <- get+ TxOut val <$> (getByteString $ fromIntegral len)++ put (TxOut o s) = do+ putWord64le o + put $ VarInt $ fromIntegral $ BS.length s+ putByteString s++-- | The OutPoint is used inside a transaction input to reference the previous+-- transaction output that it is spending.+data OutPoint = + OutPoint { + -- | The hash of the referenced transaction.+ outPointHash :: !TxHash+ -- | The position of the specific output in the transaction.+ -- The first output position is 0.+ , outPointIndex :: !Word32+ } deriving (Read, Show, Eq)++instance NFData OutPoint where+ rnf (OutPoint h i) = rnf h `seq` rnf i++instance FromJSON OutPoint where+ parseJSON = withText "outpoint" $ \t -> either fail return $+ maybeToEither "outpoint not hex" + (hexToBS $ T.unpack t) >>= decodeToEither++instance ToJSON OutPoint where+ toJSON = String . T.pack . bsToHex . encode'++instance Binary OutPoint where+ get = do+ (h,i) <- liftM2 (,) get getWord32le+ return $ OutPoint h i+ put (OutPoint h i) = put h >> putWord32le i++-- | Data type representing a variable length integer. The 'VarInt' type+-- usually precedes an array or a string that can vary in length. +newtype VarInt = VarInt { getVarInt :: Word64 }+ deriving (Eq, Show, Read)++instance NFData VarInt where+ rnf (VarInt w) = rnf w++instance Binary VarInt where++ get = VarInt <$> ( getWord8 >>= go )+ where + go 0xff = getWord64le+ go 0xfe = fromIntegral <$> getWord32le+ go 0xfd = fromIntegral <$> getWord16le+ go x = fromIntegral <$> return x++ put (VarInt x)+ | x < 0xfd = + putWord8 $ fromIntegral x+ | x <= 0xffff = do+ putWord8 0xfd+ putWord16le $ fromIntegral x+ | x <= 0xffffffff = do+ putWord8 0xfe+ putWord32le $ fromIntegral x+ | otherwise = do+ putWord8 0xff+ putWord64le x++-- | Data type for variable length strings. Variable length strings are+-- serialized as a 'VarInt' followed by a bytestring.+newtype VarString = VarString { getVarString :: BS.ByteString }+ deriving (Eq, Show, Read)++instance NFData VarString where+ rnf (VarString s) = rnf s++instance Binary VarString where++ get = VarString <$> (readBS =<< get)+ where + readBS (VarInt len) = getByteString (fromIntegral len)++ put (VarString bs) = do+ put $ VarInt $ fromIntegral $ BS.length bs+ putByteString bs++-- | When a bitcoin node creates an outgoing connection to another node,+-- the first message it will send is a 'Version' message. The other node+-- will similarly respond with it's own 'Version' message.+data Version = + Version {+ -- | Protocol version being used by the node.+ version :: !Word32+ -- | Bitmask of features to enable for this connection.+ , services :: !Word64+ -- | UNIX timestamp+ , timestamp :: !Word64+ -- | Network address of the node receiving this message.+ , addrRecv :: !NetworkAddress+ -- | Network address of the node sending this message.+ , addrSend :: !NetworkAddress+ -- | Randomly generated identifying sent with every version+ -- message. This nonce is used to detect connection to self.+ , verNonce :: !Word64+ -- | User agent+ , userAgent :: !VarString+ -- | The height of the last block received by the sending node.+ , startHeight :: !Word32+ -- | Wether the remote peer should announce relaying transactions+ -- or not. This feature is enabled since version >= 70001. See+ -- BIP37 for more details.+ , relay :: !Bool+ } deriving (Eq, Show, Read)++instance NFData Version where+ rnf (Version ver ser ts ar as vn ua sh re) =+ rnf ver `seq` rnf ser `seq` rnf ts `seq` rnf ar `seq`+ rnf as `seq` rnf vn `seq` rnf ua `seq` rnf sh `seq` rnf re++instance Binary Version where++ get = Version <$> getWord32le+ <*> getWord64le+ <*> getWord64le+ <*> get+ <*> get+ <*> getWord64le+ <*> get+ <*> getWord32le+ <*> (go =<< isEmpty)+ where + go True = return True+ go False = getBool++ put (Version v s t ar as n ua sh r) = do+ putWord32le v+ putWord64le s+ putWord64le t+ put ar+ put as+ putWord64le n+ put ua+ putWord32le sh+ putBool r++getBool :: Get Bool+getBool = go =<< getWord8+ where + go 0 = return False+ go _ = return True++putBool :: Bool -> Put +putBool True = putWord8 1+putBool False = putWord8 0++-- | A 'MessageCommand' is included in a 'MessageHeader' in order to identify+-- the type of message present in the payload. This allows the message +-- de-serialization code to know how to decode a particular message payload.+-- Every valid 'Message' constructor has a corresponding 'MessageCommand'+-- constructor.+data MessageCommand + = MCVersion + | MCVerAck + | MCAddr + | MCInv + | MCGetData + | MCNotFound + | MCGetBlocks + | MCGetHeaders + | MCTx + | MCBlock + | MCMerkleBlock+ | MCHeaders + | MCGetAddr + | MCFilterLoad+ | MCFilterAdd+ | MCFilterClear+ | MCPing + | MCPong + | MCAlert+ | MCReject+ deriving (Eq, Show, Read)++instance NFData MessageCommand++instance Binary MessageCommand where+ + get = go =<< getByteString 12+ where + go bs = case stringToCommand $ unpackCommand bs of+ Just cmd -> return cmd+ Nothing -> fail "get MessageCommand : Invalid command"++ put mc = putByteString $ packCommand $ commandToString mc+++stringToCommand :: String -> Maybe MessageCommand+stringToCommand str = case str of+ "version" -> Just MCVersion+ "verack" -> Just MCVerAck+ "addr" -> Just MCAddr+ "inv" -> Just MCInv+ "getdata" -> Just MCGetData+ "notfound" -> Just MCNotFound+ "getblocks" -> Just MCGetBlocks+ "getheaders" -> Just MCGetHeaders+ "tx" -> Just MCTx+ "block" -> Just MCBlock+ "merkleblock" -> Just MCMerkleBlock+ "headers" -> Just MCHeaders+ "getaddr" -> Just MCGetAddr+ "filterload" -> Just MCFilterLoad+ "filteradd" -> Just MCFilterAdd+ "filterclear" -> Just MCFilterClear+ "ping" -> Just MCPing+ "pong" -> Just MCPong+ "alert" -> Just MCAlert+ "reject" -> Just MCReject+ _ -> Nothing++commandToString :: MessageCommand -> String+commandToString mc = case mc of+ MCVersion -> "version"+ MCVerAck -> "verack"+ MCAddr -> "addr"+ MCInv -> "inv"+ MCGetData -> "getdata"+ MCNotFound -> "notfound"+ MCGetBlocks -> "getblocks"+ MCGetHeaders -> "getheaders"+ MCTx -> "tx"+ MCBlock -> "block"+ MCMerkleBlock -> "merkleblock"+ MCHeaders -> "headers"+ MCGetAddr -> "getaddr"+ MCFilterLoad -> "filterload"+ MCFilterAdd -> "filteradd"+ MCFilterClear -> "filterclear"+ MCPing -> "ping"+ MCPong -> "pong"+ MCAlert -> "alert"+ MCReject -> "reject"++packCommand :: String -> BS.ByteString+packCommand s = stringToBS $ take 12 $ s ++ repeat '\NUL'++unpackCommand :: BS.ByteString -> String+unpackCommand bs = bsToString $ BS.takeWhile (/= 0) bs+
− Network/Haskoin/Protocol/VarInt.hs
@@ -1,46 +0,0 @@-module Network.Haskoin.Protocol.VarInt ( VarInt(..) ) where- -import Data.Word (Word64)-import Control.Applicative ((<$>))--import Data.Binary (Binary, get, put)-import Data.Binary.Get - ( getWord8- , getWord16le- , getWord32le- , getWord64le- )-import Data.Binary.Put - ( putWord8- , putWord16le- , putWord32le- , putWord64le- )---- | Data type representing a variable length integer. The 'VarInt' type--- usually precedes an array or a string that can vary in length. -newtype VarInt = VarInt { getVarInt :: Word64 }- deriving (Eq, Show, Read)- -instance Binary VarInt where-- get = VarInt <$> ( getWord8 >>= go )- where - go 0xff = getWord64le- go 0xfe = fromIntegral <$> getWord32le- go 0xfd = fromIntegral <$> getWord16le- go x = fromIntegral <$> return x-- put (VarInt x)- | x < 0xfd = - putWord8 $ fromIntegral x- | x <= 0xffff = do- putWord8 0xfd- putWord16le $ fromIntegral x- | x <= 0xffffffff = do- putWord8 0xfe- putWord32le $ fromIntegral x- | otherwise = do- putWord8 0xff- putWord64le x-
− Network/Haskoin/Protocol/VarString.hs
@@ -1,29 +0,0 @@-module Network.Haskoin.Protocol.VarString ( VarString(..) ) where--import Control.Applicative ((<$>))--import qualified Data.ByteString as BS - ( ByteString- , length- )-import Data.Binary (Binary, get, put)-import Data.Binary.Get (getByteString)-import Data.Binary.Put (putByteString)--import Network.Haskoin.Protocol.VarInt---- | Data type for variable length strings. Variable length strings are--- serialized as a 'VarInt' followed by a bytestring.-newtype VarString = VarString { getVarString :: BS.ByteString }- deriving (Eq, Show, Read)--instance Binary VarString where-- get = VarString <$> (readBS =<< get)- where - readBS (VarInt len) = getByteString (fromIntegral len)-- put (VarString bs) = do- put $ VarInt $ fromIntegral $ BS.length bs- putByteString bs-
− Network/Haskoin/Protocol/Version.hs
@@ -1,85 +0,0 @@-module Network.Haskoin.Protocol.Version ( Version(..) ) where--import Control.Applicative ((<$>),(<*>))--import Data.Word (Word32, Word64)-import Data.Binary (Binary, get, put, Get, Put)-import Data.Binary.Get - ( getWord8- , getWord32le- , getWord64le- , isEmpty- )-import Data.Binary.Put - ( putWord8- , putWord32le- , putWord64le- )--import Network.Haskoin.Protocol.VarString-import Network.Haskoin.Protocol.NetworkAddress---- | When a bitcoin node creates an outgoing connection to another node,--- the first message it will send is a 'Version' message. The other node--- will similarly respond with it's own 'Version' message.-data Version = - Version {- -- | Protocol version being used by the node.- version :: !Word32- -- | Bitmask of features to enable for this connection.- , services :: !Word64- -- | UNIX timestamp- , timestamp :: !Word64- -- | Network address of the node receiving this message.- , addrRecv :: !NetworkAddress- -- | Network address of the node sending this message.- , addrSend :: !NetworkAddress- -- | Randomly generated identifying sent with every version- -- message. This nonce is used to detect connection to self.- , verNonce :: !Word64- -- | User agent- , userAgent :: !VarString- -- | The height of the last block received by the sending node.- , startHeight :: !Word32- -- | Wether the remote peer should announce relaying transactions- -- or not. This feature is enabled since version >= 70001. See- -- BIP37 for more details.- , relay :: !Bool- } deriving (Eq, Show, Read)--instance Binary Version where-- get = Version <$> getWord32le- <*> getWord64le- <*> getWord64le- <*> get- <*> get- <*> getWord64le- <*> get- <*> getWord32le- <*> (go =<< isEmpty)- where - go True = return True- go False = getBool-- put (Version v s t ar as n ua sh r) = do- putWord32le v- putWord64le s- putWord64le t- put ar- put as- putWord64le n- put ua- putWord32le sh- putBool r--getBool :: Get Bool-getBool = go =<< getWord8- where - go 0 = return False- go _ = return True--putBool :: Bool -> Put -putBool True = putWord8 1-putBool False = putWord8 0-
Network/Haskoin/Script.hs view
@@ -5,29 +5,41 @@ -} module Network.Haskoin.Script (+ -- *Scripts+ -- | More informations on scripts is available here:+ -- <http://en.bitcoin.it/wiki/Script>+ Script(..)+, ScriptOp(..)+, PushDataType(..)+, opPushData+ -- *Script Parsing -- **Script Outputs- ScriptOutput(..)+, ScriptOutput(..) , encodeOutput+, encodeOutputBS , decodeOutput+, decodeOutputBS , isPayPK , isPayPKHash , isPayMulSig , isPayScriptHash , scriptAddr , sortMulSig+ -- **Script Inputs , ScriptInput(..)+, SimpleInput(..)+, RedeemScript , encodeInput+, encodeInputBS , decodeInput+, decodeInputBS , isSpendPK , isSpendPKHash , isSpendMulSig- -- **ScriptHash Inputs-, ScriptHashInput(..)-, RedeemScript-, encodeScriptHash-, decodeScriptHash+, isScriptHashInput+ -- * Helpers , scriptRecipient , scriptSender@@ -49,8 +61,13 @@ , decodeSig , decodeCanonicalSig +-- *Evaluation+, evalScript+, verifySpend+, SigCheck ) where +import Network.Haskoin.Script.Types import Network.Haskoin.Script.Parser import Network.Haskoin.Script.SigHash-+import Network.Haskoin.Script.Evaluator
+ Network/Haskoin/Script/Evaluator.hs view
@@ -0,0 +1,681 @@+{-# LANGUAGE LambdaCase #-}+{-|++Module providing Bitcoin script evaluation. See+<https://github.com/bitcoin/bitcoin/blob/master/src/script.cpp>+EvalScript and <https://en.bitcoin.it/wiki/Script>++-}+module Network.Haskoin.Script.Evaluator+( +-- * Script evaluation+ verifySpend+, evalScript+, SigCheck+-- * Evaluation data types+, Program+, Stack+-- * Helper functions+, encodeInt+, decodeInt+, encodeBool+, decodeBool+, runStack+, checkStack+, dumpScript+, dumpStack+, execScript+) where++import Control.Monad.State+import Control.Monad.Error+import Control.Monad.Identity++import Control.Applicative ((<$>), (<*>))++import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BSL++import Data.List (intercalate)+import Data.Bits (shiftR, shiftL, testBit, setBit, clearBit)+import Data.Int (Int64)+import Data.Word (Word8, Word64)+import Data.Either ( rights )+import Data.Maybe ( mapMaybe, isJust )++import Network.Haskoin.Crypto+import Network.Haskoin.Script.Types+import Network.Haskoin.Script.SigHash( TxSignature(..), decodeSig, txSigHash )+import Network.Haskoin.Util ( bsToHex, decode', decodeToMaybe )+import Network.Haskoin.Protocol( Tx(..), TxIn(..) )++import Data.Binary (encode, decode)+++maxScriptSize :: Int+maxScriptSize = 10000++maxScriptElementSize :: Int+maxScriptElementSize = 520++maxStackSize :: Int+maxStackSize = 1000++maxOpcodes :: Int+maxOpcodes = 200++maxKeysMultisig :: Int+maxKeysMultisig = 20++data EvalError =+ EvalError String+ | ProgramError String Program+ | StackError ScriptOp+ | DisabledOp ScriptOp++instance Error EvalError where+ noMsg = EvalError "Evaluation Error"+ strMsg s = EvalError $ noMsg ++ " " ++ s++instance Show EvalError where+ show (EvalError m) = m+ show (ProgramError m prog) = m ++ " - program: " ++ show prog+ show (StackError op) = show op ++ ": Stack Error"+ show (DisabledOp op) = show op ++ ": disabled"++type StackValue = [Word8]+type AltStack = [StackValue]+type Stack = [StackValue]+type HashOps = [ScriptOp] -- the code that is verified by OP_CHECKSIG++-- | Defines the type of function required by script evaluating+-- functions to check transaction signatures.+type SigCheck = [ScriptOp] -> TxSignature -> PubKey -> Bool++-- | Data type of the evaluation state.+data Program = Program {+ stack :: Stack,+ altStack :: AltStack,+ hashOps :: HashOps,+ sigCheck :: SigCheck,+ opCount :: Int+}++dumpOp :: ScriptOp -> String+dumpOp (OP_PUSHDATA payload optype) =+ "OP_PUSHDATA(" ++ show optype ++ ")" +++ " 0x" ++ bsToHex payload+dumpOp op = show op++dumpList :: [String] -> String+dumpList xs = "[" ++ intercalate "," xs ++ "]"++dumpScript :: [ScriptOp] -> String+dumpScript script = dumpList $ map dumpOp script++dumpStack :: Stack -> String+dumpStack s = dumpList $ map (bsToHex . BS.pack) s++instance Show Program where+ show p = " stack: " ++ dumpStack (stack p)++type ProgramState = ErrorT EvalError Identity+type IfStack = [ Bool ]++-- | Monad of actions independent of conditional statements.+type ProgramTransition = StateT Program ProgramState+-- | Monad of actions which taking if statements into account.+-- Separate state type from ProgramTransition for type safety+type ConditionalProgramTransition a = StateT IfStack ProgramTransition a++evalProgramTransition :: ProgramTransition a -> Program -> Either EvalError a+evalProgramTransition m s = runIdentity . runErrorT $ evalStateT m s++evalConditionalProgram :: ConditionalProgramTransition a -- ^ Program monad+ -> [ Bool ] -- ^ Initial if state stack+ -> Program -- ^ Initial computation data+ -> Either EvalError a+evalConditionalProgram m s = evalProgramTransition ( evalStateT m s )++--------------------------------------------------------------------------------+-- Error utils++programError :: String -> ProgramTransition a+programError s = get >>= throwError . ProgramError s++disabled :: ScriptOp -> ProgramTransition ()+disabled op = throwError . DisabledOp $ op++--------------------------------------------------------------------------------+-- Type Conversions++-- | Encoding function for the stack value format of integers. Most+-- significant bit defines sign.+encodeInt :: Int64 -> StackValue+encodeInt i = prefix $ encode' (fromIntegral $ abs i) []+ where encode' :: Word64 -> StackValue -> StackValue+ encode' 0 bytes = bytes+ encode' j bytes = fromIntegral j:encode' (j `shiftR` 8) bytes+ prefix :: StackValue -> StackValue+ prefix [] = []+ prefix xs | testBit (last xs) 7 = prefix $ xs ++ [0]+ | i < 0 = init xs ++ [setBit (last xs) 7]+ | otherwise = xs++-- | Inverse of `encodeInt`.+decodeInt :: StackValue -> Maybe Int64+decodeInt bytes | length bytes > 4 = Nothing+ | otherwise = Just $ sign' (decodeW bytes)+ where decodeW [] = 0+ decodeW [x] = fromIntegral $ clearBit x 7+ decodeW (x:xs) = fromIntegral x + decodeW xs `shiftL` 8+ sign' i | null bytes = 0+ | testBit (last bytes) 7 = -i+ | otherwise = i++-- | Conversion of StackValue to Bool (true if non-zero).+decodeBool :: StackValue -> Bool+decodeBool [] = False+decodeBool [0x00] = False+decodeBool [0x80] = False+decodeBool (0x00:vs) = decodeBool vs+decodeBool _ = True++encodeBool :: Bool -> StackValue+encodeBool True = [1]+encodeBool False = []++constValue :: ScriptOp -> Maybe StackValue+constValue op = case op of+ OP_0 -> Just $ encodeInt 0+ OP_1 -> Just $ encodeInt 1+ OP_2 -> Just $ encodeInt 2+ OP_3 -> Just $ encodeInt 3+ OP_4 -> Just $ encodeInt 4+ OP_5 -> Just $ encodeInt 5+ OP_6 -> Just $ encodeInt 6+ OP_7 -> Just $ encodeInt 7+ OP_8 -> Just $ encodeInt 8+ OP_9 -> Just $ encodeInt 9+ OP_10 -> Just $ encodeInt 10+ OP_11 -> Just $ encodeInt 11+ OP_12 -> Just $ encodeInt 12+ OP_13 -> Just $ encodeInt 13+ OP_14 -> Just $ encodeInt 14+ OP_15 -> Just $ encodeInt 15+ OP_16 -> Just $ encodeInt 16+ OP_1NEGATE -> Just $ encodeInt $ -1+ (OP_PUSHDATA string _) -> Just $ BS.unpack string+ _ -> Nothing+++-- | Check if OpCode is constant+isConstant :: ScriptOp -> Bool+isConstant = isJust . constValue++-- | Check if OpCode is disabled+isDisabled :: ScriptOp -> Bool+isDisabled op = op `elem` [ OP_CAT+ , OP_SUBSTR+ , OP_LEFT+ , OP_RIGHT+ , OP_INVERT+ , OP_AND+ , OP_OR+ , OP_XOR+ , OP_2MUL+ , OP_2DIV+ , OP_MUL+ , OP_DIV+ , OP_MOD+ , OP_LSHIFT+ , OP_RSHIFT+ , OP_VER+ , OP_VERIF+ , OP_VERNOTIF ]++-- | Check if OpCode counts towards opcount limit++countOp :: ScriptOp -> Bool+countOp op | isConstant op = False+ | op == OP_RESERVED = False+ | otherwise = True++popInt :: ProgramTransition Int64+popInt = decodeInt <$> popStack >>= \case+ Nothing -> programError "popInt: data > nMaxNumSize"+ Just i -> return i++pushInt :: Int64 -> ProgramTransition ()+pushInt = pushStack . encodeInt++popBool :: ProgramTransition Bool+popBool = decodeBool <$> popStack++pushBool :: Bool -> ProgramTransition ()+pushBool = pushStack . encodeBool++opToSv :: StackValue -> BS.ByteString+opToSv = BS.pack++bsToSv :: BS.ByteString -> StackValue+bsToSv = BS.unpack++--------------------------------------------------------------------------------+-- Stack Primitives++getStack :: ProgramTransition Stack+getStack = stack <$> get++getCond :: ConditionalProgramTransition [Bool]+getCond = get++popCond :: ConditionalProgramTransition Bool+popCond = get >>= \condStack -> case condStack of+ [] -> lift $ programError "popCond: empty condStack"+ (c:cs) -> put cs >> return c++pushCond :: Bool -> ConditionalProgramTransition ()+pushCond c = get >>= \s ->+ put (c:s)++flipCond :: ConditionalProgramTransition ()+flipCond = popCond >>= pushCond . not++withStack :: ProgramTransition Stack+withStack = getStack >>= \case+ [] -> stackError+ s -> return s++putStack :: Stack -> ProgramTransition ()+putStack st = modify $ \p -> p { stack = st }++prependStack :: Stack -> ProgramTransition ()+prependStack s = getStack >>= \s' -> putStack $ s ++ s'++checkPushData :: ScriptOp -> ProgramTransition ()+checkPushData (OP_PUSHDATA v _) | BS.length v > fromIntegral maxScriptElementSize+ = programError "OP_PUSHDATA > maxScriptElementSize"+ | otherwise = return ()+checkPushData _ = return ()++checkStackSize :: ProgramTransition ()+checkStackSize = do n <- length <$> stack <$> get+ m <- length <$> altStack <$> get+ when ((n + m) > fromIntegral maxStackSize) $+ programError "stack > maxStackSize"++pushStack :: StackValue -> ProgramTransition ()+pushStack v = getStack >>= \s -> putStack (v:s)++popStack :: ProgramTransition StackValue+popStack = withStack >>= \(s:ss) -> putStack ss >> return s++popStackN :: Integer -> ProgramTransition [StackValue]+popStackN n | n < 0 = programError "popStackN: negative argument"+ | n == 0 = return []+ | otherwise = (:) <$> popStack <*> popStackN (n - 1)++pickStack :: Bool -> Int -> ProgramTransition ()+pickStack remove n = do+ st <- getStack++ when (n < 0) $+ programError "pickStack: n < 0"+ when (n > length st - 1) $+ programError "pickStack: n > size"++ let v = st !! n+ when remove $ putStack $ take n st ++ drop (n+1) st+ pushStack v++getHashOps :: ProgramTransition HashOps+getHashOps = hashOps <$> get++-- | Function to track the verified OPs signed by OP_CHECK(MULTI) sig.+-- Dependent on the sequence of `OP_CODESEPARATOR`+dropHashOpsSeparatedCode :: ProgramTransition ()+dropHashOpsSeparatedCode = modify $ \p ->+ let tryDrop = dropWhile ( /= OP_CODESEPARATOR ) $ hashOps p in+ case tryDrop of+ -- If no OP_CODESEPARATOR, take the whole script. This case is+ -- possible when there is no OP_CODESEPARATOR in scriptPubKey but+ -- one exists in scriptSig+ [] -> p+ _ -> p { hashOps = tail tryDrop }++-- | Filters out `OP_CODESEPARATOR` from the output script used by+-- OP_CHECK(MULTI)SIG+preparedHashOps :: ProgramTransition HashOps+preparedHashOps = filter ( /= OP_CODESEPARATOR ) <$> getHashOps++-- | Removes any PUSHDATA that contains the signatures. Used in+-- CHECK(MULTI)SIG so that signatures can be contained in output+-- scripts. See FindAndDelete() in Bitcoin Core.+findAndDelete :: [ StackValue ] -> [ ScriptOp ] -> [ ScriptOp ]+findAndDelete [] ops = ops+findAndDelete (s:ss) ops = let pushOp = opPushData . opToSv $ s in+ findAndDelete ss $ filter ( /= pushOp ) ops++checkMultiSig :: SigCheck -- ^ Signature checking function+ -> [ StackValue ] -- ^ PubKeys+ -> [ StackValue ] -- ^ Signatures+ -> [ ScriptOp ] -- ^ CODESEPARATOR'd hashops+ -> Bool+checkMultiSig f encPubKeys encSigs hOps =+ let pubKeys = mapMaybe ( decodeToMaybe . opToSv ) encPubKeys+ sigs = rights $ map ( decodeSig . opToSv ) encSigs+ cleanHashOps = findAndDelete encSigs hOps+ in (length sigs == length encSigs) && -- check for bad signatures+ orderedSatisfy (f cleanHashOps) sigs pubKeys++-- | Tests whether a function is satisfied for every a with some b "in+-- order". By "in order" we mean, if a pair satisfies the function,+-- any other satisfying pair must be deeper in each list. Designed to+-- return as soon as the result is known to minimize expensive+-- function calls. Used in checkMultiSig to verify signature/pubKey+-- pairs with a values as signatures and b values as pubkeys+orderedSatisfy :: ( a -> b -> Bool )+ -> [ a ]+ -> [ b ]+ -> Bool+orderedSatisfy _ [] _ = True+orderedSatisfy _ (_:_) [] = False+orderedSatisfy f x@(a:as) y@(b:bs) | length x > length y = False+ | f a b = orderedSatisfy f as bs+ | otherwise = orderedSatisfy f x bs++tStack1 :: (StackValue -> Stack) -> ProgramTransition ()+tStack1 f = f <$> popStack >>= prependStack++tStack2 :: (StackValue -> StackValue -> Stack) -> ProgramTransition ()+tStack2 f = f <$> popStack <*> popStack >>= prependStack++tStack3 :: (StackValue -> StackValue -> StackValue -> Stack) -> ProgramTransition ()+tStack3 f = f <$> popStack <*> popStack <*> popStack >>= prependStack++tStack4 :: (StackValue -> StackValue -> StackValue -> StackValue -> Stack)+ -> ProgramTransition ()+tStack4 f = f <$> popStack <*> popStack <*> popStack <*> popStack+ >>= prependStack++tStack6 :: (StackValue -> StackValue -> StackValue ->+ StackValue -> StackValue -> StackValue -> Stack) -> ProgramTransition ()+tStack6 f = f <$> popStack <*> popStack <*> popStack+ <*> popStack <*> popStack <*> popStack >>= prependStack++arith1 :: (Int64 -> Int64) -> ProgramTransition ()+arith1 f = do+ i <- popInt+ pushStack $ encodeInt (f i)++arith2 :: (Int64 -> Int64 -> Int64) -> ProgramTransition ()+arith2 f = do+ i <- popInt+ j <- popInt+ pushStack $ encodeInt (f i j)++stackError :: ProgramTransition a+stackError = programError "stack error"++-- AltStack Primitives++pushAltStack :: StackValue -> ProgramTransition ()+pushAltStack op = modify $ \p -> p { altStack = op:altStack p }++popAltStack :: ProgramTransition StackValue+popAltStack = get >>= \p -> case altStack p of+ a:as -> put p { altStack = as } >> return a+ [] -> programError "popAltStack: empty stack"+++incrementOpCount :: Int -> ProgramTransition ()+incrementOpCount i | i > maxOpcodes = programError "reached opcode limit"+ | otherwise = modify $ \p -> p { opCount = i + 1 }++-- Instruction Evaluation+eval :: ScriptOp -> ProgramTransition ()+eval OP_NOP = return ()+eval OP_NOP1 = return ()+eval OP_NOP2 = return ()+eval OP_NOP3 = return ()+eval OP_NOP4 = return ()+eval OP_NOP5 = return ()+eval OP_NOP6 = return ()+eval OP_NOP7 = return ()+eval OP_NOP8 = return ()+eval OP_NOP9 = return ()+eval OP_NOP10 = return ()++eval OP_VERIFY = popBool >>= \case+ True -> return ()+ False -> programError "OP_VERIFY failed"++eval OP_RETURN = programError "explicit OP_RETURN"++-- Stack++eval OP_TOALTSTACK = popStack >>= pushAltStack+eval OP_FROMALTSTACK = popAltStack >>= pushStack+eval OP_IFDUP = tStack1 $ \a -> if decodeBool a then [a, a] else [a]+eval OP_DEPTH = getStack >>= pushStack . encodeInt . fromIntegral . length+eval OP_DROP = void popStack+eval OP_DUP = tStack1 $ \a -> [a, a]+eval OP_NIP = tStack2 $ \a _ -> [a]+eval OP_OVER = tStack2 $ \a b -> [b, a, b]+eval OP_PICK = popInt >>= (pickStack False . fromIntegral)+eval OP_ROLL = popInt >>= (pickStack True . fromIntegral)+eval OP_ROT = tStack3 $ \a b c -> [c, a, b]+eval OP_SWAP = tStack2 $ \a b -> [b, a]+eval OP_TUCK = tStack2 $ \a b -> [a, b, a]+eval OP_2DROP = tStack2 $ \_ _ -> []+eval OP_2DUP = tStack2 $ \a b -> [a, b, a, b]+eval OP_3DUP = tStack3 $ \a b c -> [a, b, c, a, b, c]+eval OP_2OVER = tStack4 $ \a b c d -> [c, d, a, b, c, d]+eval OP_2ROT = tStack6 $ \a b c d e f -> [e, f, a, b, c, d]+eval OP_2SWAP = tStack4 $ \a b c d -> [c, d, a, b]++-- Splice++eval OP_SIZE = (fromIntegral . length <$> head <$> withStack) >>= pushInt++-- Bitwise Logic++eval OP_EQUAL = tStack2 $ \a b -> [encodeBool (a == b)]+eval OP_EQUALVERIFY = eval OP_EQUAL >> eval OP_VERIFY++-- Arithmetic++eval OP_1ADD = arith1 (+1)+eval OP_1SUB = arith1 (subtract 1)+eval OP_NEGATE = arith1 negate+eval OP_ABS = arith1 abs+eval OP_NOT = arith1 $ \case 0 -> 1; _ -> 0+eval OP_0NOTEQUAL = arith1 $ \case 0 -> 0; _ -> 1+eval OP_ADD = arith2 (+)+eval OP_SUB = arith2 $ flip (-)+eval OP_BOOLAND = (&&) <$> ((0 /=) <$> popInt)+ <*> ((0 /=) <$> popInt) >>= pushBool+eval OP_BOOLOR = (||) <$> ((0 /=) <$> popInt)+ <*> ((0 /=) <$> popInt) >>= pushBool+eval OP_NUMEQUAL = (==) <$> popInt <*> popInt >>= pushBool+eval OP_NUMEQUALVERIFY = eval OP_NUMEQUAL >> eval OP_VERIFY+eval OP_NUMNOTEQUAL = (/=) <$> popInt <*> popInt >>= pushBool+eval OP_LESSTHAN = (>) <$> popInt <*> popInt >>= pushBool+eval OP_GREATERTHAN = (<) <$> popInt <*> popInt >>= pushBool+eval OP_LESSTHANOREQUAL = (>=) <$> popInt <*> popInt >>= pushBool+eval OP_GREATERTHANOREQUAL = (<=) <$> popInt <*> popInt >>= pushBool+eval OP_MIN = min <$> popInt <*> popInt >>= pushInt+eval OP_MAX = max <$> popInt <*> popInt >>= pushInt+eval OP_WITHIN = within <$> popInt <*> popInt <*> popInt >>= pushBool+ where within y x a = (x <= a) && (a < y)++eval OP_RIPEMD160 = tStack1 $ return . bsToSv . hash160BS . opToSv+eval OP_SHA1 = tStack1 $ return . bsToSv . hashSha1BS . opToSv++eval OP_SHA256 = tStack1 $ return . bsToSv . hash256BS . opToSv+eval OP_HASH160 = tStack1 $ return . bsToSv . hash160BS . hash256BS . opToSv+eval OP_HASH256 = tStack1 $ return . bsToSv . doubleHash256BS . opToSv+eval OP_CODESEPARATOR = dropHashOpsSeparatedCode+eval OP_CHECKSIG = do+ pubKey <- popStack+ sig <- popStack+ checker <- sigCheck <$> get+ hOps <- preparedHashOps+ pushBool $ checkMultiSig checker [ pubKey ] [ sig ] hOps -- Reuse checkMultiSig code++eval OP_CHECKMULTISIG =+ do nPubKeys <- fromIntegral <$> popInt+ when (nPubKeys < 0 || nPubKeys > maxKeysMultisig)+ $ programError $ "nPubKeys outside range: " ++ show nPubKeys+ pubKeys <- popStackN $ toInteger nPubKeys++ nSigs <- fromIntegral <$> popInt+ when (nSigs < 0 || nSigs > nPubKeys)+ $ programError $ "nSigs outside range: " ++ show nSigs+ sigs <- popStackN $ toInteger nSigs++ void popStack -- spec bug+ checker <- sigCheck <$> get+ hOps <- preparedHashOps+ pushBool $ checkMultiSig checker pubKeys sigs hOps+ modify $ \p -> p { opCount = opCount p + length pubKeys }++eval OP_CHECKSIGVERIFY = eval OP_CHECKSIG >> eval OP_VERIFY+eval OP_CHECKMULTISIGVERIFY = eval OP_CHECKMULTISIG >> eval OP_VERIFY++eval op = case constValue op of+ Just sv -> pushStack sv+ Nothing -> programError $ "unexpected op " ++ show op++--------------------------------------------------------------------------------+-- | Based on the IfStack, returns whether the script is within an+-- evaluating if-branch.+getExec :: ConditionalProgramTransition Bool+getExec = and <$> getCond++-- | Converts a `ScriptOp` to a program monad.+conditionalEval :: ScriptOp -> ConditionalProgramTransition ()+conditionalEval scrpOp = do+ -- lift $ checkOpEnabled scrpOp+ lift $ checkPushData scrpOp++ e <- getExec+ eval' e scrpOp++ when (countOp scrpOp) $ lift $ join $ incrementOpCount <$> opCount <$> get++ lift checkStackSize++ where+ eval' :: Bool -> ScriptOp -> ConditionalProgramTransition ()++ eval' True OP_IF = lift popStack >>= pushCond . decodeBool+ eval' True OP_NOTIF = lift popStack >>= pushCond . not . decodeBool+ eval' True OP_ELSE = flipCond+ eval' True OP_ENDIF = void popCond+ eval' True op = lift $ eval op++ eval' False OP_IF = pushCond False+ eval' False OP_NOTIF = pushCond False+ eval' False OP_ELSE = flipCond+ eval' False OP_ENDIF = void popCond+ eval' False OP_CODESEPARATOR = lift $ eval OP_CODESEPARATOR+ eval' False OP_VER = return ()+ eval' False op | isDisabled op = lift $ disabled op+ | otherwise = return ()++-- | Builds a Script evaluation monad.+evalAll :: [ ScriptOp ] -> ConditionalProgramTransition ()+evalAll ops = do mapM_ conditionalEval ops+ cond <- getCond+ unless (null cond) (lift $ programError "ifStack not empty")+++checkPushOnly :: [ ScriptOp ] -> ConditionalProgramTransition ()+checkPushOnly ops+ | not (all checkPushOp ops) = lift $ programError "only push ops allowed"+ | otherwise = return ()+ where checkPushOp op = case constValue op of+ Just _ -> True+ Nothing -> False++checkStack :: Stack -> Bool+checkStack (x:_) = decodeBool x+checkStack [] = False+++isPayToScriptHash :: [ ScriptOp ] -> Bool+isPayToScriptHash [OP_HASH160, OP_PUSHDATA bytes OPCODE, OP_EQUAL]+ = BS.length bytes == 20+isPayToScriptHash _ = False++stackToScriptOps :: StackValue -> [ ScriptOp ]+stackToScriptOps sv = scriptOps $ decode $ BSL.pack sv++--+-- exported functions++execScript :: Script -- ^ scriptSig ( redeemScript )+ -> Script -- ^ scriptPubKey+ -> SigCheck -- ^ signature verification Function+ -> Either EvalError Program+execScript scriptSig scriptPubKey sigCheckFcn =+ let sigOps = scriptOps scriptSig+ pubKeyOps = scriptOps scriptPubKey+ emptyProgram = Program {+ stack = [],+ altStack = [],+ hashOps = pubKeyOps,+ sigCheck = sigCheckFcn,+ opCount = 0+ }+++ checkSig | isPayToScriptHash pubKeyOps = checkPushOnly sigOps+ | otherwise = return ()++ checkKey | BSL.length (encode scriptPubKey) > fromIntegral maxScriptSize+ = lift $ programError "pubKey > maxScriptSize"+ | otherwise = return ()+++ redeemEval = checkSig >> evalAll sigOps >> lift (stack <$> get)+ pubKeyEval = checkKey >> evalAll pubKeyOps >> lift get+ p2shEval [] = lift $ programError "PayToScriptHash: no script on stack"+ p2shEval (sv:_) = evalAll (stackToScriptOps sv) >> lift get++ in do s <- evalConditionalProgram redeemEval [] emptyProgram+ p <- evalConditionalProgram pubKeyEval [] emptyProgram { stack = s }+ if ( checkStack . runStack $ p ) && ( isPayToScriptHash pubKeyOps ) && ( not . null $ s )+ then evalConditionalProgram (p2shEval s) [] emptyProgram { stack = drop 1 s,+ hashOps = stackToScriptOps $ head s }+ else return p++evalScript :: Script -> Script -> SigCheck -> Bool+evalScript scriptSig scriptPubKey sigCheckFcn =+ case execScript scriptSig scriptPubKey sigCheckFcn of+ Left _ -> False+ Right p -> checkStack . runStack $ p++runStack :: Program -> Stack+runStack = stack++-- | A wrapper around 'verifySig' which handles grabbing the hash type+verifySigWithType :: Tx -> Int -> [ ScriptOp ] -> TxSignature -> PubKey -> Bool+verifySigWithType tx i outOps txSig pubKey =+ let outScript = Script outOps+ h = txSigHash tx outScript i ( sigHashType txSig ) in+ verifySig h ( txSignature txSig ) pubKey++-- | Uses `evalScript` to check that the input script of a spending+-- transaction satisfies the output script.+verifySpend :: Tx -- ^ The spending transaction+ -> Int -- ^ The input index+ -> Script -- ^ The output script we are spending+ -> Bool+verifySpend tx i outscript =+ let scriptSig = decode' . scriptInput $ txIn tx !! i+ verifyFcn = verifySigWithType tx i+ in+ evalScript scriptSig outscript verifyFcn
Network/Haskoin/Script/Parser.hs view
@@ -1,17 +1,19 @@ module Network.Haskoin.Script.Parser ( ScriptOutput(..) , ScriptInput(..)+, SimpleInput(..) , RedeemScript-, ScriptHashInput(..) , scriptAddr , scriptRecipient , scriptSender , encodeInput+, encodeInputBS , decodeInput+, decodeInputBS , encodeOutput+, encodeOutputBS , decodeOutput-, encodeScriptHash-, decodeScriptHash+, decodeOutputBS , sortMulSig , intToScriptOp , scriptOpToInt@@ -22,19 +24,34 @@ , isSpendPK , isSpendPKHash , isSpendMulSig+, isScriptHashInput ) where -import Control.Monad (liftM2)-import Control.Applicative ((<$>),(<*>))+import Control.DeepSeq (NFData, rnf)+import Control.Monad (liftM2, guard)+import Control.Applicative ((<$>), (<|>)) import Data.List (sortBy)-import qualified Data.ByteString as BS (head, singleton)-+import Data.Foldable (foldrM)+import qualified Data.Text as T+import qualified Data.ByteString as BS + ( ByteString+ , head+ , singleton+ )+import Data.Aeson+ ( Value (String)+ , FromJSON+ , ToJSON+ , parseJSON+ , toJSON+ , withText+ ) import Network.Haskoin.Util import Network.Haskoin.Crypto.Keys import Network.Haskoin.Crypto.Base58 import Network.Haskoin.Crypto.Hash-import Network.Haskoin.Protocol.Script+import Network.Haskoin.Script.Types import Network.Haskoin.Script.SigHash -- | Data type describing standard transaction output scripts. Output scripts@@ -51,8 +68,22 @@ } -- | Pay to a script hash. | PayScriptHash { getOutputAddress :: !Address }- deriving (Eq, Show)+ deriving (Eq, Show, Read) +instance FromJSON ScriptOutput where+ parseJSON = withText "scriptoutput" $ \t -> either fail return $+ maybeToEither "scriptoutput not hex" + (hexToBS $ T.unpack t) >>= decodeOutputBS++instance ToJSON ScriptOutput where+ toJSON = String . T.pack . bsToHex . encodeOutputBS++instance NFData ScriptOutput where+ rnf (PayPK k) = rnf k+ rnf (PayPKHash a) = rnf a+ rnf (PayMulSig k r) = rnf k `seq` rnf r+ rnf (PayScriptHash a) = rnf a+ -- | Returns True if the script is a pay to public key output. isPayPK :: ScriptOutput -> Bool isPayPK (PayPK _) = True@@ -76,9 +107,7 @@ -- | Computes a script address from a script output. This address can be used -- in a pay to script hash output. scriptAddr :: ScriptOutput -> Address-scriptAddr = ScriptAddress . hash160 . hash256BS . toBS- where - toBS = encodeScriptOps . encodeOutput +scriptAddr = ScriptAddress . hash160 . hash256BS . encodeOutputBS -- | Sorts the public keys of a multisignature output in ascending order by -- comparing their serialized representations. This feature allows for easier@@ -121,6 +150,10 @@ (PubKeyAddress _) -> error "encodeOutput: PubKeyAddress is invalid in PayScriptHash" +-- | Similar to 'encodeOutput' but encodes to a ByteString+encodeOutputBS :: ScriptOutput -> BS.ByteString+encodeOutputBS = encode' . encodeOutput+ -- | Tries to decode a 'ScriptOutput' from a 'Script'. This can fail if the -- script is not recognized as any of the standard output types. decodeOutput :: Script -> Either String ScriptOutput@@ -136,6 +169,10 @@ -- Pay to MultiSig Keys _ -> matchPayMulSig s +-- | Similar to 'decodeOutput' but decodes from a ByteString+decodeOutputBS :: BS.ByteString -> Either String ScriptOutput+decodeOutputBS = (decodeOutput =<<) . decodeToEither+ -- Match [ OP_N, PubKey1, ..., PubKeyM, OP_M, OP_CHECKMULTISIG ] matchPayMulSig :: Script -> Either String ScriptOutput matchPayMulSig (Script ops) = case splitAt (length ops - 2) ops of@@ -182,106 +219,116 @@ -- input. scriptSender :: Script -> Either String Address scriptSender s = case decodeInput s of- Right (SpendPKHash _ key) -> return $ pubKeyAddr key+ Right (RegularInput (SpendPKHash _ key)) -> return $ pubKeyAddr key+ Right (ScriptHashInput _ rdm) -> return $ scriptAddr rdm Right _ -> Left "scriptSender: bad input script type"- _ -> case decodeScriptHash s of- Right (ScriptHashInput _ rdm) -> return $ scriptAddr rdm- _ -> Left "scriptSender: non-standard script type"+ _ -> Left "scriptSender: non-standard script type" -- | Data type describing standard transaction input scripts. Input scripts -- provide the signing data required to unlock the coins of the output they are -- trying to spend. -data ScriptInput = +data SimpleInput -- | Spend the coins of a PayPK output.- SpendPK { getInputSig :: !TxSignature }+ = SpendPK { getInputSig :: !TxSignature } -- | Spend the coins of a PayPKHash output. | SpendPKHash { getInputSig :: !TxSignature , getInputKey :: !PubKey } -- | Spend the coins of a PayMulSig output.- | SpendMulSig { getInputMulSigKeys :: ![TxSignature] - , getInputMulSigRequired :: !Int- }- deriving (Eq, Show)+ | SpendMulSig { getInputMulSigKeys :: ![TxSignature] }+ deriving (Eq, Show, Read) +instance NFData SimpleInput where+ rnf (SpendPK i) = rnf i+ rnf (SpendPKHash i k) = rnf i `seq` rnf k+ rnf (SpendMulSig k) = rnf k + -- | Returns True if the input script is spending a public key. isSpendPK :: ScriptInput -> Bool-isSpendPK (SpendPK _) = True+isSpendPK (RegularInput (SpendPK _)) = True isSpendPK _ = False -- | Returns True if the input script is spending a public key hash. isSpendPKHash :: ScriptInput -> Bool-isSpendPKHash (SpendPKHash _ _) = True+isSpendPKHash (RegularInput (SpendPKHash _ _)) = True isSpendPKHash _ = False -- | Returns True if the input script is spending a multisignature output. isSpendMulSig :: ScriptInput -> Bool-isSpendMulSig (SpendMulSig _ _) = True+isSpendMulSig (RegularInput (SpendMulSig _)) = True isSpendMulSig _ = False --- | Computes a 'Script' from a 'ScriptInput'. The 'Script' is a list of --- 'ScriptOp' can can be used to build a 'Tx'.-encodeInput :: ScriptInput -> Script-encodeInput s = Script $ case s of- SpendPK ts -> [ opPushData $ encodeSig ts ]- SpendPKHash ts p -> [ opPushData $ encodeSig ts- , opPushData $ encode' p- ]- SpendMulSig ts r - | length ts <= 16 && r >= 1 && r <= 16 ->- let sigs = map (opPushData . encodeSig) ts- in OP_0 : sigs ++ replicate (r - length ts) OP_0- | otherwise -> error "SpendMulSig: Bad multisig parameters"+isScriptHashInput :: ScriptInput -> Bool+isScriptHashInput (ScriptHashInput _ _) = True+isScriptHashInput _ = False --- | Decodes a 'ScriptInput' from a 'Script'. This function fails if the --- script can not be parsed as a standard script input.-decodeInput :: Script -> Either String ScriptInput-decodeInput s = case scriptOps s of- [OP_PUSHDATA bs _] -> SpendPK <$> decodeSig bs - [OP_PUSHDATA sig _, OP_PUSHDATA p _] -> - liftM2 SpendPKHash (decodeSig sig) (decodeToEither p)- (OP_0 : xs) -> matchSpendMulSig $ Script xs- _ -> Left "decodeInput: Script did not match input templates"+type RedeemScript = ScriptOutput -matchSpendMulSig :: Script -> Either String ScriptInput-matchSpendMulSig (Script ops) = - liftM2 SpendMulSig (go ops) (return $ length ops)- where - go (OP_PUSHDATA bs _:xs) = liftM2 (:) (decodeSig bs) (go xs)- go (OP_0:xs)- | all (== OP_0) xs = return []- | otherwise = Left "matchSpendMulSig: invalid opcode after OP_0"- go [] = return []- go _ = Left "matchSpendMulSig: invalid multisig opcode"+data ScriptInput+ = RegularInput { getRegularInput :: SimpleInput }+ | ScriptHashInput { getScriptHashInput :: SimpleInput+ , getScriptHashRedeem :: RedeemScript+ }+ deriving (Eq, Show, Read) -type RedeemScript = ScriptOutput+instance NFData ScriptInput where+ rnf (RegularInput i) = rnf i+ rnf (ScriptHashInput i o) = rnf i `seq` rnf o --- | Data type describing an input script spending a pay to script hash--- output. To spend a script hash output, an input script must provide--- both a redeem script and a regular input script spending the redeem --- script.-data ScriptHashInput = ScriptHashInput - { -- | Input script spending the redeem script- spendSHInput :: ScriptInput - -- | Redeem script- , spendSHOutput :: RedeemScript- } deriving (Eq, Show)+-- | Computes a 'Script' from a 'SimpleInput'. The 'Script' is a list of +-- 'ScriptOp' that can be used to build a 'Tx'.+encodeSimpleInput :: SimpleInput -> Script+encodeSimpleInput s = Script $ case s of+ SpendPK ts -> [ opPushData $ encodeSig ts ]+ SpendPKHash ts p -> [ opPushData $ encodeSig ts+ , opPushData $ encode' p+ ]+ SpendMulSig ts -> OP_0 : map (opPushData . encodeSig) ts --- | Compute a 'Script' from a 'ScriptHashInput'. The 'Script' is a list of --- 'ScriptOp' can can be used to build a 'Tx'.-encodeScriptHash :: ScriptHashInput -> Script-encodeScriptHash (ScriptHashInput i o) =- Script $ (scriptOps si) ++ [opPushData $ encodeScriptOps so]- where - si = encodeInput i- so = encodeOutput o+decodeSimpleInput :: Script -> Either String SimpleInput+decodeSimpleInput (Script ops) = maybeToEither errMsg $+ matchPK ops <|> matchPKHash ops <|> matchMulSig ops+ where+ matchPK [OP_PUSHDATA bs _] = SpendPK <$> eitherToMaybe (decodeSig bs)+ matchPK _ = Nothing+ matchPKHash [OP_PUSHDATA sig _, OP_PUSHDATA pub _] =+ liftM2 SpendPKHash (eitherToMaybe $ decodeSig sig) (decodeToMaybe pub)+ matchPKHash _ = Nothing+ matchMulSig (x:xs) = do+ guard $ isPushOp x+ SpendMulSig <$> foldrM f [] xs+ matchMulSig _ = Nothing+ f (OP_PUSHDATA bs _) acc = + liftM2 (:) (eitherToMaybe $ decodeSig bs) (Just acc)+ f _ _ = Nothing+ errMsg = "decodeInput: Could not decode script input" --- | Tries to decode a 'ScriptHashInput' from a 'Script'. This function fails--- if the script can not be parsed as a script hash input.-decodeScriptHash :: Script -> Either String ScriptHashInput-decodeScriptHash (Script ops) = case splitAt (length ops - 1) ops of- (is,[OP_PUSHDATA bs _]) -> - ScriptHashInput <$> (decodeInput $ Script is) - <*> (decodeOutput =<< decodeScriptOps bs)- _ -> Left "decodeScriptHash: Script did not match input template"+encodeInput :: ScriptInput -> Script+encodeInput s = case s of+ RegularInput ri -> encodeSimpleInput ri+ ScriptHashInput i o -> Script $+ (scriptOps $ encodeSimpleInput i) ++ [opPushData $ encodeOutputBS o]++-- | Similar to 'encodeInput' but encodes to a ByteString+encodeInputBS :: ScriptInput -> BS.ByteString+encodeInputBS = encode' . encodeInput++-- | Decodes a 'ScriptInput' from a 'Script'. This function fails if the +-- script can not be parsed as a standard script input.+decodeInput :: Script -> Either String ScriptInput+decodeInput s@(Script ops) = maybeToEither errMsg $+ matchSimpleInput <|> matchPayScriptHash+ where+ matchSimpleInput = RegularInput <$> (eitherToMaybe $ decodeSimpleInput s)+ matchPayScriptHash = case splitAt (length (scriptOps s) - 1) ops of+ (is, [OP_PUSHDATA bs _]) -> do+ rdm <- eitherToMaybe $ decodeOutputBS bs+ inp <- eitherToMaybe $ decodeSimpleInput $ Script is+ return $ ScriptHashInput inp rdm+ _ -> Nothing+ errMsg = "decodeInput: Could not decode script input"+ +-- | Similar to 'decodeInput' but decodes from a ByteString+decodeInputBS :: BS.ByteString -> Either String ScriptInput+decodeInputBS = (decodeInput =<<) . decodeToEither
Network/Haskoin/Script/SigHash.hs view
@@ -12,12 +12,15 @@ , decodeCanonicalSig ) where -import Control.Monad (liftM2)+import Control.DeepSeq (NFData, rnf)+import Control.Monad (liftM2, mzero) import Data.Word (Word8) import Data.Bits (testBit, clearBit, setBit) import Data.Maybe (fromMaybe) import Data.Binary (Binary, get, put, getWord8, putWord8)+import Data.Aeson (Value(String), FromJSON, ToJSON, parseJSON, toJSON, withText)+import qualified Data.Text as T import qualified Data.ByteString as BS ( ByteString , index@@ -26,12 +29,14 @@ , append , pack , splitAt+ , empty ) +import Network.Haskoin.Crypto.BigWord import Network.Haskoin.Crypto.Hash import Network.Haskoin.Crypto.ECDSA-import Network.Haskoin.Protocol.Script-import Network.Haskoin.Protocol.Tx+import Network.Haskoin.Script.Types+import Network.Haskoin.Protocol import Network.Haskoin.Util -- | Data type representing the different ways a transaction can be signed.@@ -49,20 +54,26 @@ -- | Sign all of the outputs of a transaction (This is the default value). -- Changing any of the outputs of the transaction will invalidate the -- signature.- = SigAll { anyoneCanPay :: Bool } + = SigAll { anyoneCanPay :: !Bool } -- | Sign none of the outputs of a transaction. This allows anyone to -- change any of the outputs of the transaction.- | SigNone { anyoneCanPay :: Bool } + | SigNone { anyoneCanPay :: !Bool } -- | Sign only the output corresponding the the current transaction input. -- You care about your own output in the transaction but you don't -- care about any of the other outputs.- | SigSingle { anyoneCanPay :: Bool } + | SigSingle { anyoneCanPay :: !Bool } -- | Unrecognized sighash types will decode to SigUnknown.- | SigUnknown { anyoneCanPay :: Bool- , getSigCode :: Word8 + | SigUnknown { anyoneCanPay :: !Bool+ , getSigCode :: !Word8 }- deriving (Eq, Show)+ deriving (Eq, Show, Read) +instance NFData SigHash where+ rnf (SigAll a) = rnf a+ rnf (SigNone a) = rnf a+ rnf (SigSingle a) = rnf a+ rnf (SigUnknown a c) = rnf a `seq` rnf c+ -- | Returns True if the 'SigHash' has the value SigAll. isSigAll :: SigHash -> Bool isSigAll sh = case sh of@@ -103,6 +114,13 @@ SigSingle acp -> if acp then 0x83 else 0x03 SigUnknown _ w -> w +instance ToJSON SigHash where+ toJSON = String . T.pack . bsToHex . encode'++instance FromJSON SigHash where+ parseJSON = withText "sighash" $ \t ->+ maybe mzero return $ decodeToMaybe =<< (hexToBS $ T.unpack t)+ -- | Encodes a 'SigHash' to a 32 bit-long bytestring. encodeSigHash32 :: SigHash -> BS.ByteString encodeSigHash32 sh = encode' sh `BS.append` BS.pack [0,0,0]@@ -112,7 +130,7 @@ -> Script -- ^ Output script that is being spent. -> Int -- ^ Index of the input that is being signed. -> SigHash -- ^ What parts of the transaction should be signed.- -> Hash256 -- ^ Result hash to be signed.+ -> Word256 -- ^ Result hash to be signed. txSigHash tx out i sh = do let newIn = buildInputs (txIn tx) out i sh -- When SigSingle and input index > outputs, then sign integer 1@@ -124,12 +142,12 @@ -- Builds transaction inputs for computing SigHashes buildInputs :: [TxIn] -> Script -> Int -> SigHash -> [TxIn] buildInputs txins out i sh- | anyoneCanPay sh = (txins !! i) { scriptInput = out } : []+ | anyoneCanPay sh = (txins !! i) { scriptInput = encode' out } : [] | isSigAll sh || isSigUnknown sh = single | otherwise = map noSeq $ zip single [0..] where - empty = map (\ti -> ti{ scriptInput = Script [] }) txins- single = updateIndex i empty $ \ti -> ti{ scriptInput = out }+ empty = map (\ti -> ti{ scriptInput = BS.empty }) txins+ single = updateIndex i empty $ \ti -> ti{ scriptInput = encode' out } noSeq (ti,j) = if i == j then ti else ti{ txInSequence = 0 } -- Build transaction outputs for computing SigHashes@@ -140,15 +158,18 @@ | i >= length txos = Nothing | otherwise = return $ buffer ++ [txos !! i] where - buffer = replicate i $ TxOut (-1) $ Script []+ buffer = replicate i $ TxOut (-1) BS.empty -- | Data type representing a 'Signature' together with a 'SigHash'. The -- 'SigHash' is serialized as one byte at the end of a regular ECDSA -- 'Signature'. All signatures in transaction inputs are of type 'TxSignature'. data TxSignature = TxSignature - { txSignature :: Signature - , sigHashType :: SigHash- } deriving (Eq, Show)+ { txSignature :: !Signature + , sigHashType :: !SigHash+ } deriving (Eq, Show, Read)++instance NFData TxSignature where+ rnf (TxSignature s h) = rnf s `seq` rnf h -- | Serialize a 'TxSignature' to a ByteString. encodeSig :: TxSignature -> BS.ByteString
+ Network/Haskoin/Script/Types.hs view
@@ -0,0 +1,543 @@+{-# LANGUAGE DeriveGeneric #-}+module Network.Haskoin.Script.Types+( ScriptOp(..)+, Script(..)+, PushDataType(..)+, isPushOp+, opPushData+) where++import Control.DeepSeq (NFData, rnf)+import Control.Monad (liftM2, unless, forM_)+import Control.Applicative ((<$>))++import Data.Word (Word8)+import Data.Binary (Binary, get, put)+import Data.Binary.Get + ( isEmpty+ , getWord8+ , getWord16le+ , getWord32le+ , getByteString+ )+import Data.Binary.Put + ( putWord8+ , putWord16le+ , putWord32le+ , putByteString+ )+import qualified Data.ByteString as BS+ ( ByteString+ , length+ )++-- | Data type representing a transaction script. Scripts are defined as lists+-- of script operators 'ScriptOp'. Scripts are used to:+--+-- * Define the spending conditions in the output of a transaction+--+-- * Provide the spending signatures in the input of a transaction+data Script = + Script { + -- | List of script operators defining this script+ scriptOps :: ![ScriptOp] + }+ deriving (Eq, Show, Read)++instance NFData Script where+ rnf (Script o) = rnf o++instance Binary Script where+ get = + Script <$> getScriptOps+ where+ getScriptOps = do+ empty <- isEmpty+ if empty + then return [] + else liftM2 (:) get getScriptOps++ put (Script ops) = forM_ ops put++-- | Data type representing the type of an OP_PUSHDATA opcode.+data PushDataType+ = + -- | The next opcode bytes is data to be pushed onto the stack+ OPCODE + -- | The next byte contains the number of bytes to be pushed onto+ -- the stack+ | OPDATA1 + -- | The next two bytes contains the number of bytes to be pushed onto+ -- the stack+ | OPDATA2+ -- | The next four bytes contains the number of bytes to be pushed onto+ -- the stack+ | OPDATA4+ deriving (Show, Read, Eq)++instance NFData PushDataType++-- | Data type representing all of the operators allowed inside a 'Script'.+data ScriptOp+ -- Pushing Data+ = OP_PUSHDATA !BS.ByteString !PushDataType+ | OP_0+ | OP_1NEGATE+ | OP_RESERVED+ | OP_1 | OP_2 | OP_3 | OP_4+ | OP_5 | OP_6 | OP_7 | OP_8+ | OP_9 | OP_10 | OP_11 | OP_12+ | OP_13 | OP_14 | OP_15 | OP_16++ -- Flow control+ | OP_NOP+ | OP_VER -- reserved+ | OP_IF+ | OP_NOTIF+ | OP_VERIF -- resreved+ | OP_VERNOTIF -- reserved+ | OP_ELSE+ | OP_ENDIF+ | OP_VERIFY+ | OP_RETURN++ -- Stack operations+ | OP_TOALTSTACK+ | OP_FROMALTSTACK+ | OP_IFDUP+ | OP_DEPTH+ | OP_DROP+ | OP_DUP+ | OP_NIP+ | OP_OVER+ | OP_PICK+ | OP_ROLL+ | OP_ROT+ | OP_SWAP+ | OP_TUCK+ | OP_2DROP+ | OP_2DUP+ | OP_3DUP+ | OP_2OVER+ | OP_2ROT+ | OP_2SWAP++ -- Splice+ | OP_CAT+ | OP_SUBSTR+ | OP_LEFT+ | OP_RIGHT+ | OP_SIZE++ -- Bitwise logic+ | OP_INVERT+ | OP_AND+ | OP_OR+ | OP_XOR+ | OP_EQUAL+ | OP_EQUALVERIFY+ | OP_RESERVED1+ | OP_RESERVED2++ -- Arithmetic+ | OP_1ADD+ | OP_1SUB+ | OP_2MUL+ | OP_2DIV+ | OP_NEGATE+ | OP_ABS+ | OP_NOT+ | OP_0NOTEQUAL+ | OP_ADD+ | OP_SUB+ | OP_MUL+ | OP_DIV+ | OP_MOD+ | OP_LSHIFT+ | OP_RSHIFT+ | OP_BOOLAND+ | OP_BOOLOR+ | OP_NUMEQUAL+ | OP_NUMEQUALVERIFY+ | OP_NUMNOTEQUAL+ | OP_LESSTHAN+ | OP_GREATERTHAN+ | OP_LESSTHANOREQUAL+ | OP_GREATERTHANOREQUAL+ | OP_MIN+ | OP_MAX+ | OP_WITHIN++ -- Crypto+ | OP_RIPEMD160+ | OP_SHA1+ | OP_SHA256+ | OP_HASH160+ | OP_HASH256+ | OP_CODESEPARATOR+ | OP_CHECKSIG+ | OP_CHECKSIGVERIFY+ | OP_CHECKMULTISIG+ | OP_CHECKMULTISIGVERIFY++ -- Expansion+ | OP_NOP1 | OP_NOP2 | OP_NOP3 | OP_NOP4 | OP_NOP5+ | OP_NOP6 | OP_NOP7 | OP_NOP8 | OP_NOP9 | OP_NOP10+++ -- Other+ | OP_PUBKEYHASH+ | OP_PUBKEY+ | OP_INVALIDOPCODE !Word8++ deriving (Show, Read, Eq)+++instance NFData ScriptOp where+ rnf (OP_PUSHDATA b t) = rnf b `seq` rnf t+ rnf (OP_INVALIDOPCODE c) = rnf c+ rnf x = x `seq` ()+++instance Binary ScriptOp where++ get = go =<< (fromIntegral <$> getWord8) + where + go op + | op == 0x00 = return $ OP_0+ | op <= 0x4b = do+ payload <- getByteString (fromIntegral op)+ return $ OP_PUSHDATA payload OPCODE+ | op == 0x4c = do+ len <- getWord8+ payload <- getByteString (fromIntegral len)+ return $ OP_PUSHDATA payload OPDATA1+ | op == 0x4d = do+ len <- getWord16le+ payload <- getByteString (fromIntegral len)+ return $ OP_PUSHDATA payload OPDATA2+ | op == 0x4e = do+ len <- getWord32le+ payload <- getByteString (fromIntegral len)+ return $ OP_PUSHDATA payload OPDATA4++ | op == 0x4f = return $ OP_1NEGATE+ | op == 0x50 = return $ OP_RESERVED+ | op == 0x51 = return $ OP_1+ | op == 0x52 = return $ OP_2+ | op == 0x53 = return $ OP_3+ | op == 0x54 = return $ OP_4+ | op == 0x55 = return $ OP_5+ | op == 0x56 = return $ OP_6+ | op == 0x57 = return $ OP_7+ | op == 0x58 = return $ OP_8+ | op == 0x59 = return $ OP_9+ | op == 0x5a = return $ OP_10+ | op == 0x5b = return $ OP_11+ | op == 0x5c = return $ OP_12+ | op == 0x5d = return $ OP_13+ | op == 0x5e = return $ OP_14+ | op == 0x5f = return $ OP_15+ | op == 0x60 = return $ OP_16+ -- Flow control+ | op == 0x61 = return $ OP_NOP+ | op == 0x62 = return $ OP_VER -- reserved+ | op == 0x63 = return $ OP_IF+ | op == 0x64 = return $ OP_NOTIF+ | op == 0x65 = return $ OP_VERIF -- reserved+ | op == 0x66 = return $ OP_VERNOTIF -- reserved+ | op == 0x67 = return $ OP_ELSE+ | op == 0x68 = return $ OP_ENDIF+ | op == 0x69 = return $ OP_VERIFY+ | op == 0x6a = return $ OP_RETURN++ -- Stack+ | op == 0x6b = return $ OP_TOALTSTACK+ | op == 0x6c = return $ OP_FROMALTSTACK+ | op == 0x6d = return $ OP_2DROP+ | op == 0x6e = return $ OP_2DUP+ | op == 0x6f = return $ OP_3DUP+ | op == 0x70 = return $ OP_2OVER+ | op == 0x71 = return $ OP_2ROT+ | op == 0x72 = return $ OP_2SWAP+ | op == 0x73 = return $ OP_IFDUP+ | op == 0x74 = return $ OP_DEPTH+ | op == 0x75 = return $ OP_DROP+ | op == 0x76 = return $ OP_DUP+ | op == 0x77 = return $ OP_NIP+ | op == 0x78 = return $ OP_OVER+ | op == 0x79 = return $ OP_PICK+ | op == 0x7a = return $ OP_ROLL+ | op == 0x7b = return $ OP_ROT+ | op == 0x7c = return $ OP_SWAP+ | op == 0x7d = return $ OP_TUCK++ -- Splice+ | op == 0x7e = return $ OP_CAT+ | op == 0x7f = return $ OP_SUBSTR+ | op == 0x80 = return $ OP_LEFT+ | op == 0x81 = return $ OP_RIGHT+ | op == 0x82 = return $ OP_SIZE++ -- Bitwise logic+ | op == 0x83 = return $ OP_INVERT+ | op == 0x84 = return $ OP_AND+ | op == 0x85 = return $ OP_OR+ | op == 0x86 = return $ OP_XOR+ | op == 0x87 = return $ OP_EQUAL+ | op == 0x88 = return $ OP_EQUALVERIFY+ | op == 0x89 = return $ OP_RESERVED1+ | op == 0x8a = return $ OP_RESERVED2++ -- Arithmetic+ | op == 0x8b = return $ OP_1ADD+ | op == 0x8c = return $ OP_1SUB+ | op == 0x8d = return $ OP_2MUL+ | op == 0x8e = return $ OP_2DIV+ | op == 0x8f = return $ OP_NEGATE+ | op == 0x90 = return $ OP_ABS+ | op == 0x91 = return $ OP_NOT+ | op == 0x92 = return $ OP_0NOTEQUAL+ | op == 0x93 = return $ OP_ADD+ | op == 0x94 = return $ OP_SUB+ | op == 0x95 = return $ OP_MUL+ | op == 0x96 = return $ OP_DIV+ | op == 0x97 = return $ OP_MOD+ | op == 0x98 = return $ OP_LSHIFT+ | op == 0x99 = return $ OP_RSHIFT+ | op == 0x9a = return $ OP_BOOLAND+ | op == 0x9b = return $ OP_BOOLOR+ | op == 0x9c = return $ OP_NUMEQUAL+ | op == 0x9d = return $ OP_NUMEQUALVERIFY+ | op == 0x9e = return $ OP_NUMNOTEQUAL+ | op == 0x9f = return $ OP_LESSTHAN+ | op == 0xa0 = return $ OP_GREATERTHAN+ | op == 0xa1 = return $ OP_LESSTHANOREQUAL+ | op == 0xa2 = return $ OP_GREATERTHANOREQUAL+ | op == 0xa3 = return $ OP_MIN+ | op == 0xa4 = return $ OP_MAX+ | op == 0xa5 = return $ OP_WITHIN++ -- Crypto+ | op == 0xa6 = return $ OP_RIPEMD160+ | op == 0xa7 = return $ OP_SHA1+ | op == 0xa8 = return $ OP_SHA256+ | op == 0xa9 = return $ OP_HASH160+ | op == 0xaa = return $ OP_HASH256+ | op == 0xab = return $ OP_CODESEPARATOR+ | op == 0xac = return $ OP_CHECKSIG+ | op == 0xad = return $ OP_CHECKSIGVERIFY+ | op == 0xae = return $ OP_CHECKMULTISIG+ | op == 0xaf = return $ OP_CHECKMULTISIGVERIFY++ -- More NOPs+ | op == 0xb0 = return $ OP_NOP1+ | op == 0xb1 = return $ OP_NOP2+ | op == 0xb2 = return $ OP_NOP3+ | op == 0xb3 = return $ OP_NOP4+ | op == 0xb4 = return $ OP_NOP5+ | op == 0xb5 = return $ OP_NOP6+ | op == 0xb6 = return $ OP_NOP7+ | op == 0xb7 = return $ OP_NOP8+ | op == 0xb8 = return $ OP_NOP9+ | op == 0xb9 = return $ OP_NOP10++ -- Constants+ | op == 0xfd = return $ OP_PUBKEYHASH+ | op == 0xfe = return $ OP_PUBKEY++ | otherwise = return $ OP_INVALIDOPCODE op++ put op = case op of++ (OP_PUSHDATA payload optype)-> do+ let len = BS.length payload+ case optype of+ OPCODE -> do+ unless (len <= 0x4b) $ fail + "OP_PUSHDATA OPCODE: Payload size too big"+ putWord8 $ fromIntegral len+ OPDATA1 -> do+ unless (len <= 0xff) $ fail + "OP_PUSHDATA OPDATA1: Payload size too big"+ putWord8 0x4c+ putWord8 $ fromIntegral len+ OPDATA2 -> do+ unless (len <= 0xffff) $ fail + "OP_PUSHDATA OPDATA2: Payload size too big"+ putWord8 0x4d+ putWord16le $ fromIntegral len+ OPDATA4 -> do+ unless (len <= 0xffffffff) $ fail + "OP_PUSHDATA OPDATA4: Payload size too big"+ putWord8 0x4e+ putWord32le $ fromIntegral len+ putByteString payload++ -- Constants+ OP_0 -> putWord8 0x00+ OP_1NEGATE -> putWord8 0x4f+ OP_RESERVED -> putWord8 0x50+ OP_1 -> putWord8 0x51+ OP_2 -> putWord8 0x52+ OP_3 -> putWord8 0x53+ OP_4 -> putWord8 0x54+ OP_5 -> putWord8 0x55+ OP_6 -> putWord8 0x56+ OP_7 -> putWord8 0x57+ OP_8 -> putWord8 0x58+ OP_9 -> putWord8 0x59+ OP_10 -> putWord8 0x5a+ OP_11 -> putWord8 0x5b+ OP_12 -> putWord8 0x5c+ OP_13 -> putWord8 0x5d+ OP_14 -> putWord8 0x5e+ OP_15 -> putWord8 0x5f+ OP_16 -> putWord8 0x60++ -- Crypto Constants+ OP_PUBKEY -> putWord8 0xfe+ OP_PUBKEYHASH -> putWord8 0xfd++ -- Invalid Opcodes+ (OP_INVALIDOPCODE x) -> putWord8 x++ -- Flow Control+ OP_NOP -> putWord8 0x61+ OP_VER -> putWord8 0x62+ OP_IF -> putWord8 0x63+ OP_NOTIF -> putWord8 0x64+ OP_VERIF -> putWord8 0x65+ OP_VERNOTIF -> putWord8 0x66+ OP_ELSE -> putWord8 0x67+ OP_ENDIF -> putWord8 0x68+ OP_VERIFY -> putWord8 0x69+ OP_RETURN -> putWord8 0x6a++ -- Stack Operations+ OP_TOALTSTACK -> putWord8 0x6b+ OP_FROMALTSTACK -> putWord8 0x6c+ OP_2DROP -> putWord8 0x6d+ OP_2DUP -> putWord8 0x6e+ OP_3DUP -> putWord8 0x6f+ OP_2OVER -> putWord8 0x70+ OP_2ROT -> putWord8 0x71+ OP_2SWAP -> putWord8 0x72+ OP_IFDUP -> putWord8 0x73+ OP_DEPTH -> putWord8 0x74+ OP_DROP -> putWord8 0x75+ OP_DUP -> putWord8 0x76+ OP_NIP -> putWord8 0x77+ OP_OVER -> putWord8 0x78+ OP_PICK -> putWord8 0x79+ OP_ROLL -> putWord8 0x7a+ OP_ROT -> putWord8 0x7b+ OP_SWAP -> putWord8 0x7c+ OP_TUCK -> putWord8 0x7d++ -- Splice+ OP_CAT -> putWord8 0x7e+ OP_SUBSTR -> putWord8 0x7f+ OP_LEFT -> putWord8 0x80+ OP_RIGHT -> putWord8 0x81+ OP_SIZE -> putWord8 0x82++ -- Bitwise Logic+ OP_INVERT -> putWord8 0x83+ OP_AND -> putWord8 0x84+ OP_OR -> putWord8 0x85+ OP_XOR -> putWord8 0x86+ OP_EQUAL -> putWord8 0x87+ OP_EQUALVERIFY -> putWord8 0x88+ OP_RESERVED1 -> putWord8 0x89+ OP_RESERVED2 -> putWord8 0x8a++ -- Arithmetic+ OP_1ADD -> putWord8 0x8b+ OP_1SUB -> putWord8 0x8c+ OP_2MUL -> putWord8 0x8d+ OP_2DIV -> putWord8 0x8e+ OP_NEGATE -> putWord8 0x8f+ OP_ABS -> putWord8 0x90+ OP_NOT -> putWord8 0x91+ OP_0NOTEQUAL -> putWord8 0x92+ OP_ADD -> putWord8 0x93+ OP_SUB -> putWord8 0x94+ OP_MUL -> putWord8 0x95+ OP_DIV -> putWord8 0x96+ OP_MOD -> putWord8 0x97+ OP_LSHIFT -> putWord8 0x98+ OP_RSHIFT -> putWord8 0x99+ OP_BOOLAND -> putWord8 0x9a+ OP_BOOLOR -> putWord8 0x9b+ OP_NUMEQUAL -> putWord8 0x9c+ OP_NUMEQUALVERIFY -> putWord8 0x9d+ OP_NUMNOTEQUAL -> putWord8 0x9e+ OP_LESSTHAN -> putWord8 0x9f+ OP_GREATERTHAN -> putWord8 0xa0+ OP_LESSTHANOREQUAL -> putWord8 0xa1+ OP_GREATERTHANOREQUAL-> putWord8 0xa2+ OP_MIN -> putWord8 0xa3+ OP_MAX -> putWord8 0xa4+ OP_WITHIN -> putWord8 0xa5++ -- Crypto+ OP_RIPEMD160 -> putWord8 0xa6+ OP_SHA1 -> putWord8 0xa7+ OP_SHA256 -> putWord8 0xa8+ OP_HASH160 -> putWord8 0xa9+ OP_HASH256 -> putWord8 0xaa+ OP_CODESEPARATOR -> putWord8 0xab+ OP_CHECKSIG -> putWord8 0xac+ OP_CHECKSIGVERIFY -> putWord8 0xad+ OP_CHECKMULTISIG -> putWord8 0xae+ OP_CHECKMULTISIGVERIFY -> putWord8 0xaf++ -- More NOPs+ OP_NOP1 -> putWord8 0xb0+ OP_NOP2 -> putWord8 0xb1+ OP_NOP3 -> putWord8 0xb2+ OP_NOP4 -> putWord8 0xb3+ OP_NOP5 -> putWord8 0xb4+ OP_NOP6 -> putWord8 0xb5+ OP_NOP7 -> putWord8 0xb6+ OP_NOP8 -> putWord8 0xb7+ OP_NOP9 -> putWord8 0xb8+ OP_NOP10 -> putWord8 0xb9+++-- | Check whether opcode is only data.+isPushOp :: ScriptOp -> Bool+isPushOp op = case op of+ OP_PUSHDATA _ _ -> True+ OP_0 -> True+ OP_1NEGATE -> True+ OP_1 -> True+ OP_2 -> True+ OP_3 -> True+ OP_4 -> True+ OP_5 -> True+ OP_6 -> True+ OP_7 -> True+ OP_8 -> True+ OP_9 -> True+ OP_10 -> True+ OP_11 -> True+ OP_12 -> True+ OP_13 -> True+ OP_14 -> True+ OP_15 -> True+ OP_16 -> True+ _ -> False ++-- | Optimally encode data using one of the 4 types of data pushing opcodes+opPushData :: BS.ByteString -> ScriptOp+opPushData bs+ | len <= 0x4b = OP_PUSHDATA bs OPCODE+ | len <= 0xff = OP_PUSHDATA bs OPDATA1+ | len <= 0xffff = OP_PUSHDATA bs OPDATA2+ | len <= 0xffffffff = OP_PUSHDATA bs OPDATA4+ | otherwise = error "opPushData: payload size too big"+ where+ len = BS.length bs+
Network/Haskoin/Stratum.hs view
@@ -1,274 +1,232 @@-{-# LANGUAGE OverloadedStrings, FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveDataTypeable #-} module Network.Haskoin.Stratum-( -- * Types- -- ** Bitcoin- Balance(..)-, Coin(..)-, TxHeight(..)- -- ** Stratum data+( -- * Stratum JSON-RPC Message Types+ StratumRequest(..) , StratumNotif(..)-, StratumQuery(..)-, StratumResponse(..)- -- ** JSON-RPC data for Stratum-, MessageStratum-, NotifStratum-, RequestStratum-, ResponseStratum-, ResultStratum- -- ** Stratum Session for JSON-RPC Conduit-, StratumSession- -- * Functions-, toRequest-, parseResult-, parseNotif-, newStratumReq- -- * Generic JSON-RPC Conduit- -- ** Types-, Session- -- ** Functions-, initSession-, newReq-, newNotif-, reqSource-, resConduit- -- * Generic JSON-RPC Messages- -- ** Types-, Method-, ErrorValue-, RequestValue-, ResponseValue-, MessageValue-, ResultValue-, Id(..)-, Result-, Error(..)- -- ** Messages-, Request(..)-, Response(..)-, Message(..)- -- ** Errors-, errParse-, errReq-, errMeth-, errParams-, errInternal-, errStr- -- ** Helpers-, leftStr-, numericId+, StratumResult(..)+ -- * Stratum Internal Types+, StratumTxInfo(..)+, StratumCoin(..) ) where +import Control.Applicative ((<$>), (<*>))+import Control.DeepSeq (NFData, rnf) import Control.Monad (mzero)-import Control.Monad.Trans (MonadIO)+ import Data.Aeson- ( FromJSON- , ToJSON- , Value (Array, Object, String)- , (.:), (.=)- , object- , parseJSON- , toJSON- , withText- )-import Data.Aeson.Types (Parser)-import Data.Maybe (fromJust)-import Data.Text (Text, pack, unpack)-import qualified Data.Vector as V+import Data.Maybe (listToMaybe)+import Data.Text (Text)+import qualified Data.Text as T import Data.Word (Word, Word64)-import Network.Haskoin.Crypto-import Network.Haskoin.Protocol hiding (Message)-import Network.Haskoin.Stratum.Message-import Network.Haskoin.Stratum.Conduit-import Network.Haskoin.Util --- | JSON-RPC request with Stratum payload.-type RequestStratum = Request StratumQuery--- | JSON-RPC notification with Stratum payload.-type NotifStratum = Request StratumNotif--- | JSON-RPC response with Stratum payload.-type ResponseStratum = Response StratumResponse Value String--- | Stratum result in JSON-RPC response.-type ResultStratum = Result StratumResponse Value String--- | Message from Stratum JSON-RPC server.-type MessageStratum = Message StratumNotif StratumResponse Value String--- | Session type for JSON-RPC conduit.-type StratumSession- = Session RequestStratum StratumResponse Value String StratumNotif---- | Transaction height and ID pair. Used in history responses.-data TxHeight = TxHeight- { txHeightBlock :: Word -- ^ Block height.- , txHeightId :: Hash256 -- ^ Transaction id.- } deriving (Show, Eq)+import Network.JsonRpc --- | Bitcoin outpoint information.-data Coin = Coin- { coinOutPoint :: OutPoint -- ^ Coin data.- , coinTxHeight :: TxHeight -- ^ Transaction information.- , coinValue :: Word64 -- ^ Output vale.- } deriving (Show, Eq)+import Network.Haskoin.Crypto+import Network.Haskoin.Protocol --- | Balance information.-data Balance = Balance- { balConfirmed :: Word64 -- ^ Confirmed balance.- , balUnconfirmed :: Word64 -- ^ Unconfirmed balance.- } deriving (Show, Eq)+--+-- Stratum Request+-- -- | Stratum Request data. To be placed inside JSON request.-data StratumQuery- = QueryVersion { queryClientVer :: Text, queryProtoVer :: Text }- | QueryHistory { queryAddr :: Address }- | QueryBalance { queryAddr :: Address }- | QueryUnspent { queryAddr :: Address }- | QueryTx { queryTxid :: Hash256 }- | QueryBroadcast { queryTx :: Tx }- | SubAddress { queryAddr :: Address }+data StratumRequest+ = StratumReqVersion { stratumReqClientVer :: !Text+ , stratumReqProtoVer :: !Text+ }+ | StratumReqHistory { stratumReqAddr :: !Address+ }+ | StratumReqBalance { stratumReqAddr :: !Address+ }+ | StratumReqUnspent { stratumReqAddr :: !Address+ }+ | StratumReqTx { stratumReqTxid :: !TxHash+ }+ | StratumBcastTx { stratumReqTx :: !Tx+ }+ | StratumSubAddr { stratumReqAddr :: !Address+ } deriving (Eq, Show) --- | Stratum Response Result data.-data StratumResponse- = ServerVersion { stratumServerVer :: String }- | AddressHistory { stratumAddrHist :: [TxHeight] }- | AddressBalance { stratumBalance :: Balance }- | AddressUnspent { stratumCoins :: [Coin] }- | Transaction { stratumTx :: Tx }- | BroadcastId { stratumTxid :: Hash256 }- | AddrStatus { stratumAddrStatus :: Hash256 }- deriving (Eq, Show)+instance NFData StratumRequest where+ rnf (StratumReqVersion c p) = rnf c `seq` rnf p+ rnf (StratumReqHistory a) = rnf a+ rnf (StratumReqBalance a) = rnf a+ rnf (StratumReqUnspent a) = rnf a+ rnf (StratumReqTx i) = rnf i+ rnf (StratumBcastTx t) = rnf t+ rnf (StratumSubAddr a) = rnf a +instance ToJSON StratumRequest where+ toJSON (StratumReqVersion c p) = toJSON (c, p)+ toJSON (StratumReqHistory a) = toJSON [a]+ toJSON (StratumReqBalance a) = toJSON [a]+ toJSON (StratumReqUnspent a) = toJSON [a]+ toJSON (StratumReqTx i) = toJSON [i]+ toJSON (StratumBcastTx t) = toJSON [t]+ toJSON (StratumSubAddr a) = toJSON [a]++instance ToRequest StratumRequest where+ requestMethod (StratumReqVersion _ _) = "server.version"+ requestMethod (StratumReqHistory _) = "blockchain.address.get_history"+ requestMethod (StratumReqBalance _) = "blockchain.address.get_balance"+ requestMethod (StratumReqUnspent _) = "blockchain.address.listunspent"+ requestMethod (StratumReqTx _) = "blockchain.transaction.get"+ requestMethod (StratumBcastTx _) = "blockchain.transaction.broadcast"+ requestMethod (StratumSubAddr _) = "blockchain.address.subscribe"++instance FromRequest StratumRequest where+ paramsParser "server.version" = Just $ \x ->+ fmap (\(c, p) -> StratumReqVersion c p) $ parseJSON x++ paramsParser "blockchain.address.get_history" = Just $ \x ->+ parseJSON x >>= maybe mzero (return . StratumReqHistory) . listToMaybe++ paramsParser "blockchain.address.get_balance" = Just $ \x ->+ parseJSON x >>= maybe mzero (return . StratumReqBalance) . listToMaybe++ paramsParser "blockchain.address.listunspent" = Just $ \x ->+ parseJSON x >>= maybe mzero (return . StratumReqUnspent) . listToMaybe++ paramsParser "blockchain.address.subscribe" = Just $ \x ->+ parseJSON x >>= maybe mzero (return . StratumSubAddr) . listToMaybe++ paramsParser "blockchain.transaction.get" = Just $ \x ->+ parseJSON x >>= maybe mzero (return . StratumReqTx) . listToMaybe++ paramsParser "blockchain.transaction.broadcast" = Just $ \x ->+ parseJSON x >>= maybe mzero (return . StratumBcastTx) . listToMaybe++ paramsParser _ = Nothing++--+-- Stratum Notifications+--+ data StratumNotif- = NotifAddress { notifAddr :: Address, notifAddrStatus :: Hash256 }+ = StratumNotifAddr { stratumNotifAddr :: !Address+ , stratumNotifAddrStatus :: !Word256+ } deriving (Eq, Show) -instance ToJSON StratumNotif where- toJSON (NotifAddress a t) = toJSON (a, hashToJSON t)+instance NFData StratumNotif where+ rnf (StratumNotifAddr a t) = rnf a `seq` rnf t -instance ToJSON StratumQuery where- toJSON (QueryVersion c p) = toJSON (c, p)- toJSON (QueryHistory a) = toJSON [a]- toJSON (QueryUnspent a) = toJSON [a]- toJSON (QueryBalance a) = toJSON [a]- toJSON (QueryTx i) = toJSON [txidToJSON i]- toJSON (QueryBroadcast t) = txToJSON t- toJSON (SubAddress a) = toJSON [a]+instance ToJSON StratumNotif where+ toJSON (StratumNotifAddr a t) = toJSON (a, t) -instance FromJSON Balance where- parseJSON (Object o) = do- c <- o .: "confirmed"- u <- o .: "unconfirmed"- return $ Balance c u- parseJSON _ = mzero+instance ToNotif StratumNotif where+ notifMethod (StratumNotifAddr _ _) = "blockchain.address.subscribe" -instance FromJSON TxHeight where- parseJSON (Object v) = do- h <- v .: "height"- t <- v .: "tx_hash"- i <- txidParse t- return $ TxHeight h i- parseJSON _ = mzero+instance FromNotif StratumNotif where+ notifParamsParser "blockchain.address.subscribe" = Just $ \x ->+ fmap (\(a, s) -> StratumNotifAddr a s) $ parseJSON x -instance ToJSON TxHeight where- toJSON x = object- [ "height" .= txHeightBlock x- , "tx_hash" .= txidToJSON (txHeightId x)- ]+ notifParamsParser _ = Nothing -instance FromJSON Coin where- parseJSON (Object o) = do- h <- o .: "height"- v <- o .: "value"- t <- o .: "tx_hash"- p <- o .: "tx_pos"- i <- txidParse t- let op = OutPoint i p- th = TxHeight h i- return $ Coin op th v- parseJSON _ = mzero+--+-- Stratum Responses+-- -instance ToJSON Coin where- toJSON x = object- [ "height" .= txHeightBlock (coinTxHeight x)- , "value" .= coinValue x- , "tx_hash" .= txidToJSON (txHeightId $ coinTxHeight x)- , "tx_pos" .= outPointIndex (coinOutPoint x)- ]+-- | Stratum Response Result data.+data StratumResult+ = StratumSrvVersion { stratumSrvVersion :: !String+ }+ | StratumAddrHistory { stratumAddrHist :: ![StratumTxInfo]+ }+ | StratumAddrBalance { stratumConfirmed :: !Word64+ , stratumUnconfirmed :: !Word64+ }+ | StratumAddrUnspent { stratumCoins :: ![StratumCoin]+ }+ | StratumAddrStatus { stratumAddrStatus :: !Word256+ }+ | StratumTx { stratumTx :: !Tx+ }+ | StratumBcastId { stratumTxId :: !TxHash+ }+ deriving (Eq, Show) -method :: StratumQuery -> Text-method (QueryVersion _ _) = "server.version"-method (QueryHistory _) = "blockchain.address.get_history"-method (QueryBalance _) = "blockchain.address.get_balance"-method (QueryUnspent _) = "blockchain.address.get_unspent"-method (QueryTx _) = "blockchain.transaction.get"-method (QueryBroadcast _) = "blockchain.transaction.broadcast"-method (SubAddress _) = "blockchain.address.subscribe"+instance NFData StratumResult where+ rnf (StratumSrvVersion s) = rnf s+ rnf (StratumAddrHistory ts) = rnf ts+ rnf (StratumAddrBalance c u) = rnf c `seq` rnf u+ rnf (StratumAddrUnspent cs) = rnf cs+ rnf (StratumAddrStatus s) = rnf s+ rnf (StratumTx t) = rnf t+ rnf (StratumBcastId i) = rnf i --- | Create a JSON-RPC request from a Stratum request.-toRequest :: StratumQuery -- ^ Stratum request data.- -> Int -- ^ JSON-RPC request id.- -> RequestStratum -- ^ Returns JSON-RPC request object.-toRequest s i = Request (method s) (Just s) (Just (IntId i))+instance ToJSON StratumResult where+ toJSON (StratumSrvVersion v) = toJSON v+ toJSON (StratumAddrHistory ts) = toJSON ts+ toJSON (StratumAddrBalance c u) = object+ ["confirmed" .= c, "unconfirmed" .= u]+ toJSON (StratumAddrUnspent cs) = toJSON cs+ toJSON (StratumAddrStatus s) = toJSON s+ toJSON (StratumTx t) = toJSON t+ toJSON (StratumBcastId i) = toJSON i --- | Parse result from JSON-RPC response into a Stratum response.-parseResult :: StratumQuery -- ^ StratumQuery used in corresponding request.- -> ResultValue -- ^ Result from JSON-RPC response- -> Parser ResultStratum -- ^ Returns Aeson parser.-parseResult q (Right v) = parseHelper q v >>= return . Right-parseResult _ (Left e) = return $ Left e+instance FromResponse StratumResult where+ parseResult "server.version" =+ fmap StratumSrvVersion . parseJSON+ parseResult "blockchain.address.get_history" =+ fmap StratumAddrHistory . parseJSON+ parseResult "blockchain.address.get_balance" =+ withObject "balance" $ \o ->+ StratumAddrBalance <$> o .: "confirmed" <*> o .: "unconfirmed"+ parseResult "blockchain.address.listunspent" =+ fmap StratumAddrUnspent . parseJSON+ parseResult "blockchain.transaction.get" =+ fmap StratumTx . parseJSON+ parseResult "blockchain.transaction.broadcast" =+ fmap StratumBcastId . parseJSON+ parseResult "blockchain.address.subscribe" =+ fmap StratumAddrStatus . parseJSON+ parseResult m = const . fail $+ "Unknown method: " ++ T.unpack m -parseHelper :: StratumQuery -> Value -> Parser StratumResponse-parseHelper (QueryVersion _ _) v = parseJSON v >>= return . ServerVersion-parseHelper (QueryHistory _) v = parseJSON v >>= return . AddressHistory-parseHelper (QueryBalance _) v = parseJSON v >>= return . AddressBalance-parseHelper (QueryUnspent _) v = parseJSON v >>= return . AddressUnspent-parseHelper (QueryTx _) v = txParse v >>= return . Transaction-parseHelper (QueryBroadcast _) v = txidParse v >>= return . BroadcastId-parseHelper (SubAddress _) v = hashParse v >>= return . AddrStatus+--+-- Stratum Types+-- -parseNotifHelper :: Method- -> Value- -> Parser StratumNotif-parseNotifHelper "blockchain.address.subscribe" (Array v) = do- a <- parseJSON (V.head v)- s <- hashParse (V.head $ V.tail v)- return $ NotifAddress a s-parseNotifHelper _ _ = mzero+-- | Transaction height and ID pair. Used in history responses.+data StratumTxInfo = StratumTxInfo+ { stratumTxInfoHeight :: !Word -- ^ Block height.+ , stratumTxInfoId :: !TxHash -- ^ Transaction id.+ } deriving (Show, Eq) --- | Parse notification from JSON-RPC request into Stratum format.-parseNotif :: RequestValue- -- ^ Request to parse.- -> Parser NotifStratum- -- ^ Parser to Stratum request format-parseNotif (Request m (Just p) i) =- parseNotifHelper m p >>= \s -> return $ Request m (Just s) i-parseNotif _ = mzero+instance NFData StratumTxInfo where+ rnf (StratumTxInfo h i) = rnf h `seq` rnf i --- | Helper function for Network.Haskoin.JSONRPC.Conduit-newStratumReq :: MonadIO m- => StratumSession- -> StratumQuery- -> m Int-newStratumReq s q = newReq s (toRequest q) p- where- p (Response r i) = do- x <- parseResult q r- return $ Response x i+instance FromJSON StratumTxInfo where+ parseJSON = withObject "txheight" $ \o ->+ StratumTxInfo <$> o .: "height" <*> o .: "tx_hash" -txidToJSON :: Hash256 -> Value-txidToJSON = String . pack . encodeTxid+instance ToJSON StratumTxInfo where+ toJSON (StratumTxInfo h i) = object ["height" .= h, "tx_hash" .= i] -txToJSON :: Tx -> Value-txToJSON = String . pack . bsToHex . encode'+-- | Bitcoin outpoint information.+data StratumCoin = StratumCoin+ { stratumCoinOutPoint :: !OutPoint -- ^ Coin data.+ , stratumCoinTxInfo :: !StratumTxInfo -- ^ Transaction information.+ , stratumCoinValue :: !Word64 -- ^ Output vale.+ } deriving (Show, Eq) -txParse :: Value -> Parser Tx-txParse = withText "bitcoin transaction" $- return . decode' . fromJust . hexToBS . unpack+instance NFData StratumCoin where+ rnf (StratumCoin o t v) = rnf o `seq` rnf t `seq` rnf v -txidParse :: Value -> Parser Hash256-txidParse = withText "transaction id" $- return . fromJust . decodeTxid . unpack+instance FromJSON StratumCoin where+ parseJSON = withObject "coin" $ \o -> do+ h <- o .: "height"+ v <- o .: "value"+ p <- o .: "tx_pos"+ i <- o .: "tx_hash"+ return $ StratumCoin (OutPoint i p) (StratumTxInfo h i) v -hashToJSON :: Hash256 -> Value-hashToJSON = String . pack . bsToHex . encode'+instance ToJSON StratumCoin where+ toJSON (StratumCoin (OutPoint _ p) (StratumTxInfo h i) v) = object+ [ "height" .= h+ , "value" .= v+ , "tx_hash" .= i+ , "tx_pos" .= p ] -hashParse :: Value -> Parser Hash256-hashParse = withText "hash" $ return . decode' . fromJust . hexToBS . unpack
− Network/Haskoin/Stratum/Conduit.hs
@@ -1,153 +0,0 @@-{-# LANGUAGE OverloadedStrings, RankNTypes, FlexibleContexts #-}-module Network.Haskoin.Stratum.Conduit-( -- * Types- Session- -- * Functions-, initSession-, newReq-, newNotif-, reqSource-, resConduit-) where--import Prelude hiding (lines, lookup, map, map, null)-import Control.Applicative ((<$>), (<*>))-import Control.Concurrent.MVar (MVar, newMVar, putMVar, readMVar, takeMVar)-import Control.Concurrent.Chan (Chan, getChanContents, newChan, writeChan)-import Control.Monad (unless)-import Control.Monad.Trans (MonadIO, liftIO)-import Data.Aeson (ToJSON, eitherDecode, encode)-import Data.Aeson.Types (Parser, parseEither)-import Data.ByteString (ByteString)-import Data.ByteString.Lazy.Char8 (append, fromStrict, toStrict)-import Data.Conduit (Conduit, Source, ($=), (=$=), await, yield)-import Data.Conduit.List (sourceList, map)-import Data.IntMap.Strict (IntMap, delete, empty, insert, lookup, null)-import Network.Haskoin.Stratum.Message-import Network.Haskoin.Util (maybeToEither)---- | Parse JSON-RPC ResponseValue to expected Response data type.-type ResponseParser r e v = ResponseValue -> Parser (Response r e v)---- | Parse JSON-RPC RequestValue to expectedo Request data type.-type RequestParser j = RequestValue -> Parser (Request j)---- | Map of ids to result parsers.-type ParserMap r e v = IntMap (ResponseParser r e v)---- | Session state.-data Session q r e v j = Session- (Chan q) -- Requests to be sent.- (MVar Int) -- Last id of sent request.- (MVar (ParserMap r e v)) -- Map of parsers for response result data.- (Maybe (RequestParser j)) -- Parser for requests or notifications.---- | Create initial session.-initSession :: MonadIO m- => Maybe (RequestParser j)- -- ^ Parse incoming requests and notifications.- -- Keep connection open.- -> m (Session q r e v j)-initSession d = liftIO $ Session- <$> newChan- <*> newMVar 0- <*> newMVar empty- <*> return d---- | Send a new request. Goes to a channel that is read from reqSource.-newReq :: MonadIO m- => Session q r e v j -- ^ Session state.- -> (Int -> q) -- ^ Request builder.- -> ResponseParser r e v -- ^ Parser for response.- -> m Int -- ^ Output ID of sent request.-newReq (Session rc iv pv _) f g = liftIO $ do- i <- (+1) <$> takeMVar iv- p <- takeMVar pv- putMVar pv (insert i g p)- putMVar iv i- writeChan rc (f i)- return i---- | New notification, or request with no id tracking.-newNotif :: MonadIO m- => Session q r e v j -- ^ Session state.- -> q -- ^ Request to send to the network.- -> m () -- ^ No meaningful output.-newNotif (Session rc _ _ _) v = liftIO $ writeChan rc v---- | Source of requests to send to the network.-reqSource :: (MonadIO m, ToJSON q)- => Session q r e v j -- ^ Session state.- -> Source m ByteString -- ^ Source with serialized requests.-reqSource (Session rc _ _ _) = do- rs <- liftIO $ getChanContents rc- sourceList rs $= map (toStrict . flip append "\n" . encode)---- | Conduit that parses messages from network.-resConduit :: (MonadIO m)- => Session q r e v j -- ^ Session state.- -> Conduit ByteString m (Either String (Message j r e v))- -- ^ Returns Conduit with parsed data or parsing errors.-resConduit (Session _ _ pv d) =- stopOnNull pv (maybe True (const False) d) =$= decodeConduit pv d--decodeConduit :: (MonadIO m)- => MVar (ParserMap r e v)- -> Maybe (RequestParser j)- -> Conduit ByteString m (Either String (Message j r e v))-decodeConduit pv d = do- mx <- await- case mx of- Nothing -> return ()- Just x -> do- p <- liftIO $ takeMVar pv- let m = decodeMsg p d x- case m of- Left e -> yield $ Left e- Right (i, t) -> case i of- Nothing -> do- liftIO $ putMVar pv p- yield $ Right t- Just n -> do- liftIO $ putMVar pv $ n `delete` p- yield $ Right t- decodeConduit pv d--stopOnNull :: MonadIO m- => MVar (IntMap a)- -> Bool- -> Conduit i m i-stopOnNull pv d = do- b <- null <$> liftIO (readMVar pv)- unless (d && b) $ await >>= maybe e (\x -> yield x >> stopOnNull pv d)- where- e = error "Connection closed unexpectedly."--decodeMsg :: ParserMap r e v- -> Maybe (RequestParser j)- -> ByteString- -> Either String (Maybe Int, Message j r e v)-decodeMsg p d x = do- y <- eitherDecode (fromStrict x)- case y of- MsgRequest r -> do- m <- case d of- Nothing -> Left "No parser for requests"- Just l -> parseEither l r- return $ (Nothing, MsgRequest m)- MsgResponse r -> do- (i, z) <- decodeRes p r- return $ (Just i, MsgResponse z)--decodeRes :: ParserMap r e v- -> ResponseValue- -> Either String (Int, (Response r e v))-decodeRes p r = do- i <- case resId r of- Just n -> numericId n- Nothing -> Left "Id is not set."- a <- maybeToEither (e i) (lookup i p)- t <- parseEither a r- return (i, t)- where- e i = "Parser not found for response id " ++ show i ++ "."
− Network/Haskoin/Stratum/Message.hs
@@ -1,218 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--- | Implementation of basic JSON-RPC data types.-module Network.Haskoin.Stratum.Message-( -- * Types- Method-, ErrorValue-, RequestValue-, ResponseValue-, MessageValue-, ResultValue-, Id(..)-, Result-, Error(..)- -- * Messages-, Request(..)-, Response(..)-, Message(..)- -- * Errors-, errParse-, errReq-, errMeth-, errParams-, errInternal-, errStr- -- * Helpers-, leftStr-, numericId-) where--import Control.Applicative ((<|>))-import Control.Monad (mzero)-import Data.Aeson.Types hiding (Result)-import Data.Text (Text, unpack)-import Text.Read (readEither)- --- | JSON-RPC method name.-type Method = Text--- | JSON-RPC result.-type Result r e v = Either (Error e v) r--- | JSON-RPC error object with default JSON values.-type ErrorValue = Error Value String--- | JSON-RPC request with default JSON values.-type RequestValue = Request Value--- | JSON-RPC response with default JSON values.-type ResponseValue = Response Value Value String--- | JSON-RPC request or response with default JSON values.-type MessageValue = Message Value Value Value String--- | JSON-RPC result with default JSON values.-type ResultValue = Result Value Value String---- | JSON-RPC id in text or integer form.-data Id = IntId { intId :: Int } -- ^ Id in integer form.- | TxtId { txtId :: Text } -- ^ Id in string form (discouraged).- deriving (Eq, Show)---- | JSON-RPC error object in v1 or v2 format.--- Sent inside a JSONRes in case of error.-data Error e v -- | Error object in JSON-RPC version 2 format.- = ErrObj- { errCode :: Int -- ^ Integer error code.- , errMsg :: String -- ^ Error message.- , errData :: Maybe e -- ^ Optional error object.- }- -- | Error object in JSON-RPC version 1 format.- | ErrVal- { errVal :: v -- ^ Usually String.- }- deriving (Eq, Show)---- | JSON-RPC request on notification.-data Request j = Request- { reqMethod :: Method -- ^ Request method.- , reqParams :: Maybe j -- ^ Request parameters. Should be Object or Array.- , reqId :: Maybe Id -- ^ Request id. Nothing for notifications.- } deriving (Eq, Show)---- | JSON-RPC response or error.-data Response r e v = Response- { resResult :: Result r e v -- ^ Result or error.- , resId :: Maybe Id -- ^ Result id.- } deriving (Eq, Show)---- | JSON-RPC message, can contain request or response.-data Message j r e v- -- | Request message container.- = MsgRequest (Request j)- -- | response message container.- | MsgResponse (Response r e v)- deriving (Eq, Show)--instance FromJSON Id where- parseJSON (String s) = return (TxtId s)- parseJSON (Number n) = do- let (i, d) = properFraction n- if d == 0.0 then return (IntId i) else mzero- parseJSON _ = mzero--instance ToJSON Id where- toJSON (TxtId s) = toJSON s- toJSON (IntId i) = toJSON i--instance (FromJSON e, FromJSON v) => FromJSON (Error e v) where- parseJSON v@(Object o) = do- mc <- o .:? "code"- mm <- o .:? "message"- md <- o .:? "data"- case (mc, mm) of- (Just c, Just m) -> return (ErrObj c m md)- _ -> parseJSON v >>= return . ErrVal- parseJSON v = parseJSON v >>= return . ErrVal--instance (ToJSON e, ToJSON v) => ToJSON (Error e v) where- toJSON (ErrObj c m d) = object- [ "code" .= c- , "message" .= m- , "data" .= d ]- toJSON (ErrVal v) = toJSON v--instance FromJSON j => FromJSON (Request j) where- parseJSON (Object o) = do- m <- o .: "method"- p <- o .:? "params"- i <- o .:? "id"- return (Request m p i)- parseJSON _ = mzero--instance ToJSON j => ToJSON (Request j) where- toJSON (Request m p i) = object $ filter f- [ "jsonrpc" .= ("2.0" :: String)- , "method" .= m- , "params" .= p- , "id" .= i ]- where- f (_, Null) = False- f _ = True--instance (FromJSON r, FromJSON e, FromJSON v)- => FromJSON (Response r e v)- where- parseJSON (Object o) = do- mi <- o .: "id"- me <- o .:? "error" .!= Nothing- mr <- o .:? "result" .!= Nothing- case (me, mr) of- (Just e, _) -> return (Response (Left e) mi)- (_, Just r) -> return (Response (Right r) mi)- _ -> mzero- parseJSON _ = mzero--instance (ToJSON r, ToJSON e, ToJSON v)- => ToJSON (Response r e v)- where- toJSON (Response (Right r) i) = object- [ "jsonrpc" .= ("2.0" :: String)- , "id" .= i- , "result" .= r- ]- toJSON (Response (Left e) i) = object- [ "jsonrpc" .= ("2.0" :: String)- , "id" .= i- , "error" .= e- ]--instance (FromJSON j, FromJSON r, FromJSON e, FromJSON v)- => FromJSON (Message j r e v)- where- parseJSON o@(Object _) = q <|> s- where- q = parseJSON o >>= return . MsgRequest- s = parseJSON o >>= return . MsgResponse- parseJSON _ = mzero--instance (ToJSON j, ToJSON r, ToJSON e, ToJSON v)- => ToJSON (Message j r e v)- where- toJSON (MsgRequest r) = toJSON r- toJSON (MsgResponse r) = toJSON r---- | Parse error in JSON-RPC v2 format.--- Provide optional error object.-errParse :: ToJSON e => Maybe e -> Error e v-errParse = ErrObj (-32700) "Parse error"---- | Request error in JSON-RPC v2 format.--- Provide optional error object.-errReq :: ToJSON e => Maybe e -> Error e v-errReq = ErrObj (-32600) "Invalid request"---- | Unknown method error in JSON-RPC v2 format.--- Provide optional error object.-errMeth :: ToJSON e => Maybe e -> Error e v-errMeth = ErrObj (-32601) "Method not found"---- | Invalid parameters error in JSON-RPC v2 format.--- Provide optional error object.-errParams :: ToJSON e => Maybe e -> Error e v-errParams = ErrObj (-32602) "Invalid params"---- | Internal error in JSON-RPC v2 format.--- Provide optional error object.-errInternal :: ToJSON e => Maybe e -> Error e v-errInternal = ErrObj (-32606) "Internal error"---- | Get string from error object.-errStr :: Error e Value -> String-errStr (ErrObj _ m _) = m-errStr (ErrVal v) = either id id . flip parseEither v $- withText "error string" (return . unpack)---- | Map Left error objects to strings.-leftStr :: Either (Error e Value) r -> Either String r-leftStr (Left e) = Left (errStr e)-leftStr (Right r) = Right r---- | Force an id into a number or fail if not possible.-numericId :: Id -> Either String Int-numericId (IntId i) = Right i-numericId (TxtId t) = readEither $ unpack t
Network/Haskoin/Transaction.hs view
@@ -11,8 +11,11 @@ -- *Transaction signing , SigInput(..) , signTx+, signInput , detSignTx-, isTxComplete+, detSignInput+, verifyStdTx+, verifyStdInput -- *Coin selection , Coin(..)
Network/Haskoin/Transaction/Builder.hs view
@@ -1,42 +1,75 @@+{-# LANGUAGE OverloadedStrings #-} module Network.Haskoin.Transaction.Builder ( Coin(..) , buildTx , buildAddrTx , SigInput(..) , signTx+, signInput , detSignTx-, verifyTx+, detSignInput+, verifyStdTx+, verifyStdInput , guessTxSize , chooseCoins , chooseMSCoins , getFee , getMSFee-, isTxComplete ) where -import Control.Monad (when, guard, liftM2)-import Control.Applicative ((<$>))-import Control.Monad.Trans (lift)+import Control.Applicative ((<$>),(<*>))+import Control.Monad (mzero, foldM)+import Control.Monad.Trans (lift)+import Control.Monad.Trans.Either (EitherT, left)+import Control.DeepSeq (NFData, rnf) -import Data.Maybe (catMaybes, maybeToList, fromMaybe)-import Data.List (sortBy, find)+import Data.Maybe (catMaybes, maybeToList, isJust, fromJust, isNothing)+import Data.List (sortBy, find, nub) import Data.Word (Word64)-import qualified Data.ByteString as BS (length, replicate)+import qualified Data.ByteString as BS (length, replicate, empty)+import Data.Aeson+ ( Value (Object)+ , FromJSON+ , ToJSON+ , (.=), (.:), (.:?)+ , object+ , parseJSON+ , toJSON+ ) import Network.Haskoin.Util import Network.Haskoin.Crypto import Network.Haskoin.Protocol import Network.Haskoin.Script --- | A Coin is something that can be spent by a transaction and is--- represented by a transaction output, an outpoint and optionally a--- redeem script.+-- | A Coin is an output of a transaction that can be spent by another+-- transaction. data Coin = - Coin { coinTxOut :: TxOut -- ^ Transaction output- , coinOutPoint :: OutPoint -- ^ Previous outpoint- , coinRedeem :: Maybe Script -- ^ Redeem script- } deriving (Eq, Show)+ Coin { coinValue :: !Word64 -- ^ Value in satoshi+ , coinScript :: !ScriptOutput -- ^ Output script+ , coinOutPoint :: !OutPoint -- ^ Previous outpoint+ , coinRedeem :: !(Maybe RedeemScript) -- ^ Redeem script+ } deriving (Eq, Show, Read) +instance ToJSON Coin where+ toJSON (Coin v s o r) = object+ [ "value" .= v+ , "script" .= s+ , "outpoint" .= o+ , "redeem" .= r+ ]++instance FromJSON Coin where+ parseJSON (Object o) =+ Coin <$> o .: "value"+ <*> o .: "script"+ <*> o .: "outpoint"+ <*> o .: "redeem"+ parseJSON _ = mzero++instance NFData Coin where+ rnf (Coin v s o r) = rnf v `seq` rnf s `seq` rnf o `seq` rnf r+ -- | Coin selection algorithm for normal (non-multisig) transactions. This -- function returns the selected coins together with the amount of change to -- send back to yourself, taking the fee into account.@@ -68,7 +101,7 @@ -- Select coins greedily by starting from an empty solution greedyAdd :: Word64 -> (Int -> Word64) -> [Coin] -> Maybe ([Coin],Word64) greedyAdd target fee xs = go [] 0 [] 0 $ sortBy desc xs- where desc a b = compare (outValue $ coinTxOut b) (outValue $ coinTxOut a)+ where desc a b = compare (coinValue b) (coinValue a) goal c = target + fee c go _ _ [] _ [] = Nothing go _ _ ps pTot [] = return (ps,pTot - (goal $ length ps))@@ -78,24 +111,7 @@ then go [] 0 (y:acc) (aTot + val) ys else return (ps,pTot - (goal $ length ps)) | otherwise = go (y:acc) (aTot + val) ps pTot ys- where val = outValue $ coinTxOut y--{---- Start from a solution containing all coins and greedily remove them-greedyRem :: Word64 -> (Int -> Word64) -> [Coin] -> Maybe ([Coin],Word64)-greedyRem target fee xs - | s < goal (length xs) = Nothing- | otherwise = return $ go [] s $ sortBy desc xs- where desc a b = compare (outValue $ coinTxOut b) (outValue $ coinTxOut a)- s = sum $ map (outValue . coinTxOut) xs- goal c = target + fee c- go acc tot [] = (acc,tot - (goal $ length acc))- go acc tot (y:ys) - | tot - val >= (goal $ length ys + length acc) = - go acc (tot - val) ys- | otherwise = go (y:acc) tot ys- where val = outValue $ coinTxOut y--}+ where val = coinValue y getFee :: Word64 -> Int -> Word64 getFee kbfee count = kbfee*((len + 999) `div` 1000)@@ -151,212 +167,209 @@ -- and a list of 'ScriptOutput' and amounts as outputs. buildTx :: [OutPoint] -> [(ScriptOutput,Word64)] -> Either String Tx buildTx xs ys = mapM fo ys >>= \os -> return $ Tx 1 (map fi xs) os 0- where fi outPoint = TxIn outPoint (Script []) maxBound- fo (o,v) | v <= 2100000000000000 = return $ TxOut v $ encodeOutput o+ where fi outPoint = TxIn outPoint BS.empty maxBound+ fo (o,v) | v <= 2100000000000000 = return $ TxOut v $ encodeOutputBS o | otherwise = Left $ "buildTx: Invalid amount " ++ (show v) -- | Data type used to specify the signing parameters of a transaction input. -- To sign an input, the previous output script, outpoint and sighash are -- required. When signing a pay to script hash output, an additional redeem -- script is required.-data SigInput - -- | Parameters for signing a pay to public key hash output.- = SigInput { sigDataOut :: Script -- ^ Output script to spend.- , sigDataOP :: OutPoint - -- ^ Reference to the transaction output to spend.- , sigDataSH :: SigHash -- ^ Signature type.- } - -- | Parameters for signing a pay to script hash output.- | SigInputSH { sigDataOut :: Script - , sigDataOP :: OutPoint - , sigRedeem :: Script -- ^ Redeem script.- , sigDataSH :: SigHash - } deriving (Eq, Show)+data SigInput = SigInput + { sigDataOut :: !ScriptOutput -- ^ Output script to spend. + , sigDataOP :: !OutPoint -- ^ Spending tranasction OutPoint+ , sigDataSH :: !SigHash -- ^ Signature type.+ , sigDataRedeem :: !(Maybe RedeemScript) -- ^ Redeem script+ } deriving (Eq, Read, Show) -liftSecret :: Monad m => Build a -> SecretT (BuildT m) a-liftSecret = lift . liftBuild+instance NFData SigInput where+ rnf (SigInput o p h b) = rnf o `seq` rnf p `seq` rnf h `seq` rnf b --- | Returns True if all the inputs of a transactions are non-empty and if--- all multisignature inputs are fully signed.-isTxComplete :: Tx -> Bool-isTxComplete = isComplete . (mapM toBuildTxIn) . txIn+instance ToJSON SigInput where+ toJSON (SigInput so op sh rdm) = object $+ [ "pkscript" .= so+ , "outpoint" .= op+ , "sighash" .= sh+ ] ++ if isNothing rdm then [] else [ "redeem" .= fromJust rdm ] +instance FromJSON SigInput where+ parseJSON (Object o) = do+ so <- o .: "pkscript"+ op <- o .: "outpoint"+ sh <- o .: "sighash"+ rdm <- o .:? "redeem"+ return $ SigInput so op sh rdm+ parseJSON _ = mzero+ -- | Sign a transaction by providing the 'SigInput' signing parameters and a -- list of private keys. The signature is computed within the 'SecretT' monad--- to generate the random signing nonce and within the 'BuildT' monad to add--- information on wether the result was fully or partially signed.+-- to generate the random signing nonce. This function returns a transaction+-- completion status. If false, some of the inputs are not fully signed or are+-- non-standard. signTx :: Monad m - => Tx -- ^ Transaction to sign- -> [SigInput] -- ^ SigInput signing parameters- -> [PrvKey] -- ^ List of private keys to use for signing- -> SecretT (BuildT m) Tx -- ^ Signed transaction-signTx tx@(Tx _ ti _ _) sigis keys = do- liftSecret $ when (null ti) $ Broken "signTx: Transaction has no inputs"- newIn <- mapM sign $ orderSigInput ti sigis- return tx{ txIn = newIn }- where sign (maybeSI,txin,i) = case maybeSI of- Just sigi -> signTxIn txin sigi tx i keys- _ -> liftSecret $ toBuildTxIn txin+ => Tx -- ^ Transaction to sign+ -> [SigInput] -- ^ SigInput signing parameters+ -> [PrvKey] -- ^ List of private keys to use for signing+ -> EitherT String (SecretT m) (Tx, Bool) + -- ^ (Signed transaction, Status)+signTx otx@(Tx _ ti _ _) sigis allKeys + | null ti = left "signTx: Transaction has no inputs"+ | otherwise = do+ tx <- foldM go otx $ findSigInput sigis ti+ return (tx, verifyStdTx tx sigDat)+ where + sigDat = map (\(SigInput so op _ _) -> (so, op)) sigis+ go tx (sigi@(SigInput so _ _ rdmM), i) = do+ keys <- liftEither $ sigKeys so rdmM allKeys+ if null keys+ then return tx+ else foldM (\t k -> fst <$> signInput t i sigi k) tx keys -signTxIn :: Monad m => TxIn -> SigInput -> Tx -> Int -> [PrvKey] - -> SecretT (BuildT m) TxIn-signTxIn txin sigi tx i keys = do- (out,vKeys,pubs,buildf) <- liftSecret $ decodeSigInput sigi keys- let msg = txSigHash tx (encodeOutput out) i sh- sigs <- mapM (signMsg msg) vKeys- liftSecret $ buildf txin tx out i pubs $ map (flip TxSignature sh) sigs- where sh = sigDataSH sigi+-- | Sign a single input in a transaction within the 'SecretT' monad. This +-- function will return a completion status only for that input. If false, +-- that input is either non-standard or not fully signed.+signInput :: Monad m => Tx -> Int -> SigInput -> PrvKey + -> EitherT String (SecretT m) (Tx, Bool)+signInput tx i (SigInput so _ sh rdmM) key = do+ sig <- flip TxSignature sh <$> lift (signMsg msg key)+ si <- liftEither $ buildInput tx i so rdmM sig $ derivePubKey key+ let newTx = tx{ txIn = updateIndex i (txIn tx) (f si) }+ return (newTx, verifyStdInput newTx i so)+ where+ f si x = x{ scriptInput = encodeInputBS si }+ msg | isJust rdmM = txSigHash tx (encodeOutput $ fromJust rdmM) i sh+ | otherwise = txSigHash tx (encodeOutput so) i sh -- | Sign a transaction by providing the 'SigInput' signing paramters and -- a list of private keys. The signature is computed deterministically as--- defined in RFC-6979. The signature is computed within the 'Build' monad--- to add information on wether the result was fully or partially signed.-detSignTx :: Tx -- ^ Transaction to sign- -> [SigInput] -- ^ SigInput signing parameters- -> [PrvKey] -- ^ List of private keys to use for signing- -> Build Tx -- ^ Signed transaction-detSignTx tx@(Tx _ ti _ _) sigis keys = do- when (null ti) $ Broken "detSignTx: Transaction has no inputs"- newIn <- mapM sign $ orderSigInput ti sigis- return tx{ txIn = newIn }+-- defined in RFC-6979. This function returns a transaction completion status.+-- If false, some of the inputs are not fully signed or are non-standard.+detSignTx :: Tx -- ^ Transaction to sign+ -> [SigInput] -- ^ SigInput signing parameters+ -> [PrvKey] -- ^ List of private keys to use for signing+ -> Either String (Tx, Bool) + -- ^ (Signed transaction, Status)+detSignTx otx@(Tx _ ti _ _) sigis allKeys+ | null ti = Left "signTx: Transaction has no inputs"+ | otherwise = do+ tx <- foldM go otx $ findSigInput sigis ti+ return (tx, verifyStdTx tx sigDat) where - sign (maybeSI,txin,i) - | isComplete txinB = txinB- | otherwise = case maybeSI of- Just sigi -> detSignTxIn txin sigi tx i keys- _ -> txinB- where- txinB = toBuildTxIn txin- -detSignTxIn :: TxIn -> SigInput -> Tx -> Int -> [PrvKey] -> Build TxIn-detSignTxIn txin sigi tx i keys = do- (out,vKeys,pubs,buildf) <- decodeSigInput sigi keys- let msg = txSigHash tx (encodeOutput out) i sh- sigs = map (detSignMsg msg) vKeys- buildf txin tx out i pubs $ map (flip TxSignature sh) sigs- where sh = sigDataSH sigi--{- Helpers for signing transactions -}---- |Decides if a TxIn is complete. If the TxIn could not be decoded and it--- is not empty, we consider it complete.-toBuildTxIn :: TxIn -> Build TxIn-toBuildTxIn txin@(TxIn _ s _)- | null $ scriptOps s = Partial txin- | otherwise = case decodeScriptHash s of- Right (ScriptHashInput (SpendMulSig xs r) _) -> - guardPartial (length xs == r) >> return txin- Right _ -> return txin- Left _ -> case decodeInput s of- Right (SpendMulSig xs r) ->- guardPartial (length xs == r) >> return txin- _ -> return txin--orderSigInput :: [TxIn] -> [SigInput] -> [(Maybe SigInput, TxIn, Int)]-orderSigInput ti si = zip3 (matchTemplate si ti f) ti [0..]- where f s txin = sigDataOP s == prevOutput txin--type BuildFunction = TxIn -> Tx -> ScriptOutput -> Int - -> [PubKey] -> [TxSignature] -> Build TxIn--decodeSigInput :: SigInput -> [PrvKey] -> - Build (ScriptOutput, [PrvKey], [PubKey], BuildFunction)-decodeSigInput sigi keys = case sigi of- SigInput s _ _ -> do- out <- eitherToBuild $ decodeOutput s- (vKeys,pubs) <- sigKeys out keys- return (out,vKeys,pubs,buildTxIn)- SigInputSH s _ sr _ -> do- out <- eitherToBuild $ decodeOutput s- rdm <- eitherToBuild $ decodeOutput sr- (vKeys,pubs) <- sigKeysSH out rdm keys- return (rdm,vKeys,pubs,buildTxInSH)+ sigDat = map (\(SigInput so op _ _) -> (so, op)) sigis+ go tx (sigi@(SigInput so _ _ rdmM), i) = do+ keys <- sigKeys so rdmM allKeys+ if null keys+ then return tx+ else foldM (\t k -> fst <$> detSignInput t i sigi k) tx keys -buildTxInSH :: BuildFunction-buildTxInSH txin tx rdm i pubs sigs = do- s <- scriptInput <$> buildTxIn txin tx rdm i pubs sigs- res <- either emptyIn return $ - encodeScriptHash . (flip ScriptHashInput rdm) <$> decodeInput s- return txin{ scriptInput = res }- where emptyIn = const $ Partial $ Script []+-- | Sign a single input in a transaction deterministically (RFC-6979). This+-- function will return a completion status only for that input. If false, +-- that input is either non-standard or not fully signed.+detSignInput :: Tx -> Int -> SigInput -> PrvKey -> Either String (Tx, Bool)+detSignInput tx i (SigInput so _ sh rdmM) key = do+ let sig = TxSignature (detSignMsg msg key) sh+ si <- buildInput tx i so rdmM sig $ derivePubKey key+ let newTx = tx{ txIn = updateIndex i (txIn tx) (f si) }+ return (newTx, verifyStdInput newTx i so)+ where+ f si x = x{ scriptInput = encodeInputBS si }+ msg | isJust rdmM = txSigHash tx (encodeOutput $ fromJust rdmM) i sh+ | otherwise = txSigHash tx (encodeOutput so) i sh -buildTxIn :: BuildFunction-buildTxIn txin tx out i pubs sigs - | null sigs = Partial txin{ scriptInput = Script [] }- | otherwise = buildRes <$> case out of- PayPK _ -> return $ SpendPK $ head sigs- PayPKHash _ -> return $ SpendPKHash (head sigs) (head pubs)- PayMulSig msPubs r -> do- let mSigs = take r $ catMaybes $ matchTemplate aSigs msPubs f- guardPartial $ length mSigs == r- return $ SpendMulSig mSigs r- _ -> Broken "buildTxIn: Can't sign a P2SH script here"- where buildRes res = txin{ scriptInput = encodeInput res }- aSigs = concat- [ sigs - , case decodeScriptHash $ scriptInput txin of- Right (ScriptHashInput (SpendMulSig xs _) _) -> xs- _ -> case decodeInput $ scriptInput txin of- Right (SpendMulSig xs _) -> xs- _ -> []- ]- f (TxSignature sig sh) pub = - verifySig (txSigHash tx (encodeOutput out) i sh) sig pub+-- Order the SigInput with respect to the transaction inputs. This allow the+-- users to provide the SigInput in any order. Users can also provide only a+-- partial set of SigInputs.+findSigInput :: [SigInput] -> [TxIn] -> [(SigInput, Int)]+findSigInput si ti = + catMaybes $ map g $ zip (matchTemplate si ti f) [0..]+ where + f s txin = sigDataOP s == prevOutput txin+ g (Just s, i) = Just (s,i)+ g (Nothing, _) = Nothing -sigKeysSH :: ScriptOutput -> RedeemScript -> [PrvKey]- -> Build ([PrvKey],[PubKey])-sigKeysSH out rdm keys = case out of- PayScriptHash a -> if scriptAddr rdm == a- then sigKeys rdm keys- else Broken "sigKeys: Redeem script does not match P2SH script"- _ -> Broken "sigKeys: Can only decode P2SH script here"+-- Find from the list of private keys which one is required to sign the +-- provided ScriptOutput. +sigKeys :: ScriptOutput -> (Maybe RedeemScript) -> [PrvKey] + -> Either String [PrvKey]+sigKeys so rdmM keys = do+ case (so, rdmM) of+ (PayPK p, Nothing) -> return $ + map fst $ maybeToList $ find ((== p) . snd) zipKeys+ (PayPKHash a, Nothing) -> return $ + map fst $ maybeToList $ find ((== a) . pubKeyAddr . snd) zipKeys+ (PayMulSig ps r, Nothing) -> return $ + map fst $ take r $ filter ((`elem` ps) . snd) zipKeys+ (PayScriptHash _, Just rdm) ->+ sigKeys rdm Nothing keys+ _ -> Left "sigKeys: Could not decode output script" + where+ zipKeys = zip keys (map derivePubKey keys) -sigKeys :: ScriptOutput -> [PrvKey] -> Build ([PrvKey],[PubKey])-sigKeys out keys = unzip <$> case out of- PayPK p -> return $ maybeToList $ - find ((== p) . snd) zipKeys- PayPKHash a -> return $ maybeToList $ - find ((== a) . pubKeyAddr . snd) zipKeys- PayMulSig ps r -> return $ take r $ - filter ((`elem` ps) . snd) zipKeys- _ -> Broken "sigKeys: Can't decode P2SH here" - where zipKeys = zip keys $ map derivePubKey keys+-- Construct an input, given a signature and a public key+buildInput :: Tx -> Int -> ScriptOutput -> (Maybe RedeemScript) + -> TxSignature -> PubKey -> Either String ScriptInput+buildInput tx i so rdmM sig pub = case (so, rdmM) of+ (PayPK _, Nothing) -> + return $ RegularInput $ SpendPK sig+ (PayPKHash _, Nothing) -> + return $ RegularInput $ SpendPKHash sig pub+ (PayMulSig msPubs r, Nothing) -> do+ let mSigs = take r $ catMaybes $ matchTemplate allSigs msPubs f+ return $ RegularInput $ SpendMulSig mSigs+ (PayScriptHash _, Just rdm) -> do+ inp <- buildInput tx i rdm Nothing sig pub+ return $ ScriptHashInput (getRegularInput inp) rdm+ _ -> Left "buildInput: Invalid output/redeem script combination"+ where + scp = scriptInput $ txIn tx !! i+ allSigs = nub $ sig : case decodeInputBS scp of+ Right (ScriptHashInput (SpendMulSig xs) _) -> xs+ Right (RegularInput (SpendMulSig xs)) -> xs+ _ -> []+ out = encodeOutput so+ f (TxSignature x sh) p = verifySig (txSigHash tx out i sh) x p {- Tx verification -} --- This is not the final transaction verification function. It is here mainly--- as a helper for tests. It can only validates standard inputs.-verifyTx :: Tx -> [(Script,OutPoint)] -> Bool-verifyTx tx xs = flip all z3 $ \(maybeS,txin,i) -> fromMaybe False $ do- (out,inp) <- maybeS >>= flip decodeVerifySigInput txin- let so = encodeOutput out- case (out,inp) of- (PayPK pub,SpendPK (TxSignature sig sh)) -> - return $ verifySig (txSigHash tx so i sh) sig pub- (PayPKHash a,SpendPKHash (TxSignature sig sh) pub) -> do- guard $ pubKeyAddr pub == a- return $ verifySig (txSigHash tx so i sh) sig pub- (PayMulSig pubs r,SpendMulSig sigs _) ->- (== r) <$> countMulSig tx so i pubs sigs - _ -> Nothing- where m = map (fst <$>) $ matchTemplate xs (txIn tx) f- f (_,o) txin = o == prevOutput txin- z3 = zip3 m (txIn tx) [0..]+-- | Verify if a transaction is valid and all of its inputs are standard.+verifyStdTx :: Tx -> [(ScriptOutput, OutPoint)] -> Bool+verifyStdTx tx xs = + all go $ zip (matchTemplate xs (txIn tx) f) [0..]+ where+ f (_,o) txin = o == prevOutput txin+ go (Just (so,_), i) = verifyStdInput tx i so+ go _ = False++-- | Verify if a transaction input is valid and standard.+verifyStdInput :: Tx -> Int -> ScriptOutput -> Bool+verifyStdInput tx i so' = + go (scriptInput $ txIn tx !! i) so'+ where+ go inp so = case decodeInputBS inp of+ Right (RegularInput (SpendPK (TxSignature sig sh))) -> + let pub = getOutputPubKey so+ in verifySig (txSigHash tx out i sh) sig pub+ Right (RegularInput (SpendPKHash (TxSignature sig sh) pub)) ->+ let a = getOutputAddress so+ in pubKeyAddr pub == a && + verifySig (txSigHash tx out i sh) sig pub+ Right (RegularInput (SpendMulSig sigs)) ->+ let pubs = getOutputMulSigKeys so+ r = getOutputMulSigRequired so+ in countMulSig tx out i pubs sigs == r+ Right (ScriptHashInput si rdm) ->+ scriptAddr rdm == getOutputAddress so && + go (encodeInputBS $ RegularInput si) rdm+ _ -> False+ where+ out = encodeOutput so -- Count the number of valid signatures-countMulSig :: Tx -> Script -> Int -> [PubKey] -> [TxSignature] -> Maybe Int-countMulSig _ _ _ [] _ = return 0-countMulSig _ _ _ _ [] = return 0-countMulSig tx so i (pub:pubs) sigs@(TxSignature sig sh:rest)- | verifySig (txSigHash tx so i sh) sig pub = - (+1) <$> countMulSig tx so i pubs rest- | otherwise = countMulSig tx so i pubs sigs- -decodeVerifySigInput :: Script -> TxIn -> Maybe (ScriptOutput, ScriptInput)-decodeVerifySigInput so (TxIn _ si _ ) = case decodeOutput so of- Right (PayScriptHash a) -> do- (ScriptHashInput inp rdm) <- eitherToMaybe $ decodeScriptHash si- guard $ scriptAddr rdm == a- return (rdm,inp)- out -> eitherToMaybe $ liftM2 (,) out (decodeInput si)+countMulSig :: Tx -> Script -> Int -> [PubKey] -> [TxSignature] -> Int+countMulSig _ _ _ [] _ = 0+countMulSig _ _ _ _ [] = 0+countMulSig tx out i (pub:pubs) sigs@(TxSignature sig sh:rest)+ | verifySig (txSigHash tx out i sh) sig pub = + 1 + countMulSig tx out i pubs rest+ | otherwise = countMulSig tx out i pubs sigs
Network/Haskoin/Util.hs view
@@ -46,40 +46,13 @@ , snd3 , lst3 - -- *Build monad-, Build(..)-, isComplete-, isPartial-, isBroken-, eitherToBuild-, buildToEither-, guardPartial-- -- *BuildT transformer monad-, BuildT(..)-, liftBuild-- -- *Constants-, addrPrefix-, scriptPrefix-, secretPrefix-, extPubKeyPrefix-, extSecretPrefix-, networkMagic-, genesisHeader-, maxBlockSize-, walletFile-, haskoinUserAgent- ) where import Numeric (readHex) import Control.Applicative ((<$>)) import Control.Monad (guard) import Control.Monad.Trans.Either (EitherT, hoistEither)-import Text.Printf (printf) -import Data.Monoid (mappend) import Data.Word (Word8) import Data.Bits ((.|.), shiftL, shiftR) import Data.List (unfoldr)@@ -108,17 +81,17 @@ ( ByteString , concat , pack, unpack- , foldl' , null )+import qualified Data.ByteString.Builder as BSB+ ( toLazyByteString+ , byteStringHex+ ) import qualified Data.ByteString.Char8 as C ( pack , unpack ) -import Network.Haskoin.Util.BuildMonad-import Network.Haskoin.Util.Constants- -- ByteString helpers -- | Transforms a lazy bytestring into a strict bytestring@@ -155,7 +128,7 @@ -- | Encode a bytestring to a base16 (HEX) representation bsToHex :: BS.ByteString -> String-bsToHex = BS.foldl' (\a x -> mappend a (printf "%02x" x)) ""+bsToHex = bsToString . toStrictBS . BSB.toLazyByteString . BSB.byteStringHex -- | Decode a base16 (HEX) string from a bytestring. This function can fail -- if the string contains invalid HEX characters@@ -164,8 +137,10 @@ where hexWord x = do guard $ length x == 2- let (w, s) = head $ readHex x- guard $ s == ""+ let hs = readHex x+ guard $ not $ null hs+ let [(w, s)] = hs+ guard $ null s return w -- Data.Binary helpers@@ -296,7 +271,7 @@ -- Various helpers -- | Applies a function to only one element of a list defined by it's index.--- If the index is out of the bounds of the list, the original list is returned+-- If the index is out of the bounds of the list, the original list is returned. updateIndex :: Int -- ^ The index of the element to change -> [a] -- ^ The list of elements -> (a -> a) -- ^ The function to apply@@ -321,12 +296,15 @@ (l,(r:rs)) -> (Just r) : matchTemplate (l ++ rs) bs f _ -> Nothing : matchTemplate as bs f +-- | Returns the first value of a triple. fst3 :: (a,b,c) -> a fst3 (a,_,_) = a +-- | Returns the second value of a triple. snd3 :: (a,b,c) -> b snd3 (_,b,_) = b +-- | Returns the last value of a triple. lst3 :: (a,b,c) -> c lst3 (_,_,c) = c
− Network/Haskoin/Util/BuildMonad.hs
@@ -1,136 +0,0 @@-{-|- The Build type, and associated operations.--}-module Network.Haskoin.Util.BuildMonad -( Build(..)-, BuildT(..)-, isComplete-, isPartial-, isBroken-, eitherToBuild-, buildToEither-, guardPartial-, liftBuild-) where--import Control.Monad (liftM)-import Control.Monad.Trans - ( MonadTrans- , MonadIO- , lift- , liftIO- )--{-|- The Build monad represents computations that can be in one of three states:- - * Complete-- * Partial-- * Broken-- It extends the Either monad with an additional Partial value to describe a- valid computation flagged with a Partial context. The Build monad is useful- when you describe computations where parts of the computation are either- complete, partially complete or broken. Combining only Complete computations- will produce a Complete result. However, if one of the computations is- Partial, the whole computation will be Partial as well. And if some- computation is Broken, the whole computation will be broken as well.-- The Build monad is used by Haskoin to describe the state of the transaction- signing computation. To sign a transaction, all input scripts need to be - signed. The whole transaction will be completely signed only if all the- input scripts are completely signed. If any of the inputs is partially signed,- then the whole transaction will be partially signed as well. And the whole- transaction is broken if one of the inputs failed to parse or is broken.--}-data Build a - -- | Describes a successful complete computation- = Complete { runBuild :: a } - -- | Describes a successful but partial computation- | Partial { runBuild :: a }- -- | Describes a broken computation- | Broken { runBroken :: String }- deriving Eq--instance Show a => Show (Build a) where- show (Complete a) = "Complete " ++ (show a)- show (Partial a) = "Partial " ++ (show a)- show (Broken str) = "Broken " ++ str--instance Functor Build where- fmap f (Complete x) = Complete (f x)- fmap f (Partial x) = Partial (f x)- fmap _ (Broken s) = Broken s--instance Monad Build where- return = Complete- Complete x >>= f = f x- Partial x >>= f = case f x of- e@(Broken _) -> e- a -> Partial $ runBuild a- Broken s >>= _ = Broken s---- | Returns True if the Build value is Complete-isComplete :: Build a -> Bool-isComplete (Complete _) = True-isComplete _ = False---- | Returns True if the Build value is Partial-isPartial :: Build a -> Bool-isPartial (Partial _) = True-isPartial _ = False---- | Return True if the Build value is Broken-isBroken :: Build a -> Bool-isBroken (Broken _) = True-isBroken _ = False---- | Transforms an Either String value into a Build value. Right is mapped to--- Complete and Left is mapped to Broken-eitherToBuild :: Either String a -> Build a-eitherToBuild m = case m of- Left err -> Broken err - Right res -> Complete res---- | Transforms a Build value into an Either String value. Complete and Partial--- are mapped to Right and Broken is mapped to Left.-buildToEither :: Build a -> Either String a-buildToEither m = case m of- Complete a -> Right a- Partial a -> Right a- Broken err -> Left err---- | Binds a Partial value to the computation when the predicate is False.-guardPartial :: Bool -> Build ()-guardPartial True = Complete ()-guardPartial False = Partial ()---- | BuildT transformer monad-newtype BuildT m a = BuildT { runBuildT :: m (Build a) }--mapBuildT :: (m (Build a) -> n (Build b)) -> BuildT m a -> BuildT n b-mapBuildT f = BuildT . f . runBuildT--instance Functor m => Functor (BuildT m) where- fmap f = mapBuildT (fmap (fmap f))--instance Monad m => Monad (BuildT m) where- return = lift . return- x >>= f = BuildT $ do- v <- runBuildT x- case v of Complete a -> runBuildT (f a)- Partial a -> runBuildT (f a)- Broken str -> return $ Broken str--instance MonadTrans BuildT where- lift = BuildT . liftM Complete--instance MonadIO m => MonadIO (BuildT m) where- liftIO = lift . liftIO---- | Lift a Build computation into the BuildT monad-liftBuild :: Monad m => Build a -> BuildT m a-liftBuild = BuildT . return-
− Network/Haskoin/Util/Constants.hs
@@ -1,55 +0,0 @@-{-|- Declaration of constants used across the bitcoin protocol.--}-module Network.Haskoin.Util.Constants where--import Data.Word (Word8,Word32)---- | Prefix for base58 PubKey hash address-addrPrefix :: Word8-addrPrefix = 0---- | Prefix for base58 script hash address-scriptPrefix :: Word8-scriptPrefix = 5---- | Prefix for private key WIF format-secretPrefix :: Word8-secretPrefix = 128---- | Prefix for extended public keys (BIP32)-extPubKeyPrefix :: Word32-extPubKeyPrefix = 0x0488b21e---- | Prefix for extended private keys (BIP32)-extSecretPrefix :: Word32-extSecretPrefix = 0x0488ade4---- | Network magic bytes-networkMagic :: Word32-networkMagic = 0xf9beb4d9 ---- | Genesis block header information-genesisHeader :: [Integer]-genesisHeader =- -- Hash = 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f- [ 0x01- , 0x00- , 0x3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a- , 1231006505- , 486604799- , 2083236893- ]---- | Maximum size of a block in bytes-maxBlockSize :: Int-maxBlockSize = 1000000---- | Wallet database file name-walletFile :: String-walletFile = "walletdb"---- | User agent of this haskoin package-haskoinUserAgent :: String-haskoinUserAgent = "/haskoin:0.0.2.1/"-
− Network/Haskoin/Util/Constants/Testnet.hs
@@ -1,55 +0,0 @@-{-|- Declaration of constants used across the testnet bitcoin protocol--}-module Network.Haskoin.Util.Constants.Testnet where--import Data.Word (Word8,Word32)---- | Prefix for base58 PubKey hash address-addrPrefix :: Word8-addrPrefix = 111---- | Prefix for base58 script hash address-scriptPrefix :: Word8-scriptPrefix = 196---- | Prefix for private key WIF format-secretPrefix :: Word8-secretPrefix = 239---- | Prefix for extended public keys (BIP32)-extPubKeyPrefix :: Word32-extPubKeyPrefix = 0x043587cf---- | Prefix for extended private keys (BIP32)-extSecretPrefix :: Word32-extSecretPrefix = 0x04358394---- | Network magic bytes-networkMagic :: Word32-networkMagic = 0x0b110907 ---- | Genesis block header information-genesisHeader :: [Integer]-genesisHeader =- -- Hash = 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943- [ 0x01- , 0x00- , 0x3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a- , 1296688602- , 486604799- , 414098458- ]---- | Maximum size of a block in bytes-maxBlockSize :: Int-maxBlockSize = 1000000---- | Wallet database file name-walletFile :: String-walletFile = "testwalletdb"---- | User agent of this haskoin package-haskoinUserAgent :: String-haskoinUserAgent = "/haskoin:0.0.2.1/"-
haskoin.cabal view
@@ -1,5 +1,5 @@ name: haskoin-version: 0.0.2.1+version: 0.1.0 synopsis: Implementation of the Bitcoin protocol. description: Haskoin is a package providing an implementation of the Bitcoin protocol@@ -7,8 +7,8 @@ functions for handling BIP32 extended keys, functions for building and signing various types of regular and multisig transactions and a definition of the network protocol messages.-homepage: http://github.com/plaprade/haskoin-bug-reports: http://github.com/plaprade/haskoin/issues+homepage: http://github.com/haskoin/haskoin+bug-reports: http://github.com/haskoin/haskoin/issues stability: experimental license: PublicDomain license-file: UNLICENSE@@ -17,12 +17,17 @@ category: Bitcoin, Finance, Network build-type: Simple cabal-version: >= 1.9.2-extra-source-files: tests/data/*.json+extra-source-files: tests/data/*.json,+ testnet/Network/Haskoin/Constants.hs,+ prodnet/Network/Haskoin/Constants.hs source-repository head type: git- location: git://github.com/plaprade/haskoin.git+ location: git://github.com/haskoin/haskoin.git +Flag Testnet+ Description: Compile the library against Testnet instead of Prodnet+ Default: False library exposed-modules: Network.Haskoin.Util,@@ -30,11 +35,10 @@ Network.Haskoin.Crypto, Network.Haskoin.Protocol, Network.Haskoin.Script,- Network.Haskoin.Transaction- other-modules: Network.Haskoin.Util.BuildMonad,- Network.Haskoin.Util.Constants,- Network.Haskoin.Util.Constants.Testnet,- Network.Haskoin.Crypto.NumberTheory, + Network.Haskoin.Transaction,+ Network.Haskoin.Internals,+ Network.Haskoin.Constants+ other-modules: Network.Haskoin.Crypto.NumberTheory, Network.Haskoin.Crypto.Curve, Network.Haskoin.Crypto.Hash, Network.Haskoin.Crypto.BigWord,@@ -48,50 +52,31 @@ Network.Haskoin.Crypto.Merkle, Network.Haskoin.Crypto.Bloom, Network.Haskoin.Protocol.Message,- Network.Haskoin.Protocol.VarInt,- Network.Haskoin.Protocol.BlockHeader,- Network.Haskoin.Protocol.Addr, - Network.Haskoin.Protocol.Tx, - Network.Haskoin.Protocol.NotFound, - Network.Haskoin.Protocol.NetworkAddress, - Network.Haskoin.Protocol.Inv, - Network.Haskoin.Protocol.VarString, - Network.Haskoin.Protocol.GetBlocks, - Network.Haskoin.Protocol.Ping, - Network.Haskoin.Protocol.Script, - Network.Haskoin.Protocol.InvVector, - Network.Haskoin.Protocol.Alert, - Network.Haskoin.Protocol.MessageHeader, - Network.Haskoin.Protocol.Block, - Network.Haskoin.Protocol.MerkleBlock, - Network.Haskoin.Protocol.BloomFilter, - Network.Haskoin.Protocol.Version, - Network.Haskoin.Protocol.GetData, - Network.Haskoin.Protocol.Headers, - Network.Haskoin.Protocol.GetHeaders,+ Network.Haskoin.Protocol.Types,+ Network.Haskoin.Script.Types, Network.Haskoin.Script.Parser, Network.Haskoin.Script.SigHash,- Network.Haskoin.Stratum.Message,- Network.Haskoin.Stratum.Conduit,+ Network.Haskoin.Script.Evaluator, Network.Haskoin.Transaction.Builder- build-depends: aeson >= 0.7 && < 0.8,- base >= 4.6 && < 4.7, - binary >= 0.7 && < 0.8, - byteable >= 0.1 && < 0.2,- bytestring >= 0.10 && < 0.11, - conduit >= 1.1 && < 1.2,- conduit-extra >= 1.1 && < 1.2,- containers >= 0.5 && < 0.6,- cryptohash >= 0.11 && < 0.12,- either >= 4.0 && < 4.1,- mtl >= 2.1 && < 2.2, - pbkdf >= 1.1 && < 1.2,- scientific >= 0.2 && < 0.3,- split >= 0.2 && < 0.3,- text >= 0.11 && < 0.12,- text-icu >= 0.6 && < 0.7,- vector >= 0.10 && < 0.11+ build-depends: aeson >= 0.8 && < 0.9,+ base >= 4.6 && < 5, + binary >= 0.7 && < 0.8, + byteable >= 0.1 && < 0.2,+ bytestring >= 0.10.4 && < 0.11, + containers >= 0.5 && < 0.6,+ cryptohash >= 0.11 && < 0.12,+ deepseq >= 1.3 && < 1.4,+ either >= 4.3 && < 4.4,+ json-rpc >= 0.2.0.1 && < 0.3,+ mtl >= 2.2 && < 2.3, + pbkdf >= 1.1 && < 1.2,+ split >= 0.2 && < 0.3,+ text >= 1.1 && < 1.2 ghc-options: -Wall -fno-warn-orphans+ if flag(testnet)+ hs-source-dirs: . testnet+ else+ hs-source-dirs: . prodnet test-suite test-haskoin type: exitcode-stdio-1.0@@ -102,6 +87,7 @@ Network.Haskoin.Crypto.Point.Tests, Network.Haskoin.Crypto.ECDSA.Tests, Network.Haskoin.Crypto.Base58.Tests,+ Network.Haskoin.Crypto.Base58.Units, Network.Haskoin.Crypto.Keys.Tests, Network.Haskoin.Crypto.ExtendedKeys.Tests, Network.Haskoin.Crypto.ExtendedKeys.Units,@@ -124,28 +110,33 @@ Network.Haskoin.Transaction.Tests, Network.Haskoin.Transaction.Units, Network.Haskoin.Transaction.Arbitrary,- Network.Haskoin.Stratum.Units- build-depends: aeson >= 0.7 && < 0.8,- base >= 4.6 && < 4.7, - binary >= 0.7 && < 0.8, - byteable >= 0.1 && < 0.2,- bytestring >= 0.10 && < 0.11, - conduit >= 1.1 && < 1.2,- conduit-extra >= 1.1 && < 1.2,- containers >= 0.5 && < 0.6,- cryptohash >= 0.11 && < 0.12,- either >= 4.0 && < 4.1,- mtl >= 2.1 && < 2.2, - pbkdf >= 1.1 && < 1.2,- scientific >= 0.2 && < 0.3,- split >= 0.2 && < 0.3,- text >= 0.11 && < 0.12,- text-icu >= 0.6 && < 0.7,- HUnit >= 1.2 && < 1.3,- QuickCheck >= 2.6 && < 2.7,- test-framework >= 0.8 && < 0.9, - test-framework-quickcheck2 >= 0.3 && < 0.4, - test-framework-hunit >= 0.3 && < 0.4 - hs-source-dirs: . tests+ Network.Haskoin.Stratum.Tests,+ Network.Haskoin.Stratum.Units,+ Network.Haskoin.Stratum.Arbitrary,+ Network.Haskoin.Json.Tests+ build-depends: aeson >= 0.8 && < 0.9,+ async >= 2.0 && < 2.1,+ base >= 4.6 && < 5, + binary >= 0.7 && < 0.8, + byteable >= 0.1 && < 0.2,+ bytestring >= 0.10.4 && < 0.11, + containers >= 0.5 && < 0.6,+ cryptohash >= 0.11 && < 0.12,+ deepseq >= 1.3 && < 1.4,+ either >= 4.3 && < 4.4,+ json-rpc >= 0.2.0.1 && < 0.3,+ mtl >= 2.2 && < 2.3, + pbkdf >= 1.1 && < 1.2,+ split >= 0.2 && < 0.3,+ text >= 1.1 && < 1.2,+ HUnit >= 1.2 && < 1.3,+ QuickCheck >= 2.7 && < 2.8,+ test-framework >= 0.8 && < 0.9, + test-framework-quickcheck2 >= 0.3 && < 0.4, + test-framework-hunit >= 0.3 && < 0.4 ghc-options: -Wall -fno-warn-orphans+ if flag(testnet)+ hs-source-dirs: . tests testnet+ else+ hs-source-dirs: . tests prodnet
+ prodnet/Network/Haskoin/Constants.hs view
@@ -0,0 +1,85 @@+{-|+ Network specific constants+-}+module Network.Haskoin.Constants where++import Data.Word (Word8,Word32)+import Network.Haskoin.Crypto.BigWord+import Network.Haskoin.Protocol.Types++-- | Name of the bitcoin network+networkName :: String+networkName = "prodnet"++-- | Prefix for base58 PubKey hash address+addrPrefix :: Word8+addrPrefix = 0++-- | Prefix for base58 script hash address+scriptPrefix :: Word8+scriptPrefix = 5++-- | Prefix for private key WIF format+secretPrefix :: Word8+secretPrefix = 128++-- | Prefix for extended public keys (BIP32)+extPubKeyPrefix :: Word32+extPubKeyPrefix = 0x0488b21e++-- | Prefix for extended private keys (BIP32)+extSecretPrefix :: Word32+extSecretPrefix = 0x0488ade4++-- | Network magic bytes+networkMagic :: Word32+networkMagic = 0xf9beb4d9 ++-- | Genesis block header information+genesisHeader :: BlockHeader+genesisHeader = BlockHeader+ -- Hash = 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f+ { blockVersion = 0x01+ , prevBlock = 0x00+ , merkleRoot = + 0x3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a+ , blockTimestamp = 1231006505+ , blockBits = 486604799+ , bhNonce = 2083236893+ }++-- | Maximum size of a block in bytes+maxBlockSize :: Int+maxBlockSize = 1000000++-- | User agent of this haskoin package+haskoinUserAgent :: String+haskoinUserAgent = "/haskoin:0.1.0/"++-- | Default port+defaultPort :: Int+defaultPort = 8333++-- | Should we allow relaxed difficulty transition rules. This is useful+-- for testnet.+allowMinDifficultyBlocks :: Bool+allowMinDifficultyBlocks = False++-- | Checkpoints to enfore+checkpoints :: [(Int, BlockHash)]+checkpoints =+ -- These are in little endian notation!+ [ ( 11111, 0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d)+ , ( 33333, 0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6)+ , ( 74000, 0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20)+ , (105000, 0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97)+ , (134444, 0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe)+ , (168000, 0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763)+ , (193000, 0x000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317)+ , (210000, 0x000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e)+ , (216116, 0x00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e)+ , (225430, 0x00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932)+ , (250000, 0x000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214)+ , (279000, 0x0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40)+ ]+
+ testnet/Network/Haskoin/Constants.hs view
@@ -0,0 +1,73 @@+{-|+ Network specific constants+-}+module Network.Haskoin.Constants where++import Data.Word (Word8,Word32)+import Network.Haskoin.Crypto.BigWord+import Network.Haskoin.Protocol.Types++-- | Name of the bitcoin network+networkName :: String+networkName = "testnet"++-- | Prefix for base58 PubKey hash address+addrPrefix :: Word8+addrPrefix = 111++-- | Prefix for base58 script hash address+scriptPrefix :: Word8+scriptPrefix = 196++-- | Prefix for private key WIF format+secretPrefix :: Word8+secretPrefix = 239++-- | Prefix for extended public keys (BIP32)+extPubKeyPrefix :: Word32+extPubKeyPrefix = 0x043587cf++-- | Prefix for extended private keys (BIP32)+extSecretPrefix :: Word32+extSecretPrefix = 0x04358394++-- | Network magic bytes+networkMagic :: Word32+networkMagic = 0x0b110907 ++-- | Genesis block header information+genesisHeader :: BlockHeader+genesisHeader = BlockHeader+ -- Hash = 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943+ { blockVersion = 0x01+ , prevBlock = 0x00+ , merkleRoot = + 0x3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a+ , blockTimestamp = 1296688602+ , blockBits = 486604799+ , bhNonce = 414098458+ }++-- | Maximum size of a block in bytes+maxBlockSize :: Int+maxBlockSize = 1000000++-- | User agent of this haskoin package+haskoinUserAgent :: String+haskoinUserAgent = "/haskoin-testnet:0.1.0/"++-- | Default port+defaultPort :: Int+defaultPort = 18333++-- | Should we allow relaxed difficulty transition rules. This is useful+-- for testnet.+allowMinDifficultyBlocks :: Bool+allowMinDifficultyBlocks = True++-- | Checkpoints to enfore+checkpoints :: [(Int, BlockHash)]+checkpoints =+ -- These are in little endian notation!+ [ (546, 0x000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70) ]+
tests/Main.hs view
@@ -10,6 +10,7 @@ import qualified Network.Haskoin.Crypto.Point.Tests (tests) import qualified Network.Haskoin.Crypto.ECDSA.Tests (tests) import qualified Network.Haskoin.Crypto.Base58.Tests (tests)+import qualified Network.Haskoin.Crypto.Base58.Units (tests) import qualified Network.Haskoin.Crypto.Keys.Tests (tests) import qualified Network.Haskoin.Crypto.ExtendedKeys.Tests (tests) import qualified Network.Haskoin.Crypto.ExtendedKeys.Units (tests)@@ -36,8 +37,12 @@ import qualified Network.Haskoin.Transaction.Units (tests) -- Stratum tests+import qualified Network.Haskoin.Stratum.Tests (tests) import qualified Network.Haskoin.Stratum.Units (tests) +-- Json tests+import qualified Network.Haskoin.Json.Tests (tests)+ main :: IO () main = defaultMain ( Network.Haskoin.Util.Tests.tests@@ -45,6 +50,7 @@ ++ Network.Haskoin.Crypto.Point.Tests.tests ++ Network.Haskoin.Crypto.ECDSA.Tests.tests ++ Network.Haskoin.Crypto.Base58.Tests.tests + ++ Network.Haskoin.Crypto.Base58.Units.tests ++ Network.Haskoin.Crypto.Hash.Tests.tests ++ Network.Haskoin.Crypto.Hash.Units.tests ++ Network.Haskoin.Crypto.Keys.Tests.tests @@ -63,6 +69,8 @@ ++ Network.Haskoin.Script.Units.tests ++ Network.Haskoin.Transaction.Tests.tests ++ Network.Haskoin.Transaction.Units.tests+ ++ Network.Haskoin.Stratum.Tests.tests ++ Network.Haskoin.Stratum.Units.tests+ ++ Network.Haskoin.Json.Tests.tests )
tests/Network/Haskoin/Crypto/Arbitrary.hs view
@@ -18,7 +18,6 @@ import Data.Maybe import Network.Haskoin.Crypto.Point-import Network.Haskoin.Crypto.Hash import Network.Haskoin.Crypto.BigWord import Network.Haskoin.Crypto.ECDSA import Network.Haskoin.Crypto.Keys@@ -36,9 +35,6 @@ instance BigWordMod n => Arbitrary (BigWord n) where arbitrary = arbitrarySizedBoundedIntegral--instance Arbitrary CheckSum32 where- arbitrary = chksum32 <$> arbitrary instance Arbitrary Point where arbitrary = frequency
+ tests/Network/Haskoin/Crypto/Base58/Units.hs view
@@ -0,0 +1,44 @@+module Network.Haskoin.Crypto.Base58.Units (tests) where++import Test.HUnit (Assertion, assertBool)+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)++import qualified Data.ByteString as BS++import Network.Haskoin.Crypto.Base58+import Network.Haskoin.Util++tests :: [Test]+tests =+ [ testGroup "Test base58 encodings" + ( map mapBase58Vec $ zip vectors [0..] )+ ]++mapBase58Vec :: ((BS.ByteString, String, String), Int) -> Test.Framework.Test+mapBase58Vec (v,i) = + testCase (unwords [ "Test base58 vector", show i ]) $ runVector v++runVector :: (BS.ByteString, String, String) -> Assertion+runVector (bs, e, chk) = do+ assertBool "encodeBase58" $ e == bsToString b58+ assertBool "encodeBase58Check" $ chk == bsToString b58Chk+ assertBool "decodeBase58" $ Just bs == decodeBase58 b58+ assertBool "decodeBase58Check" $ Just bs == decodeBase58Check b58Chk+ where+ b58 = encodeBase58 bs+ b58Chk = encodeBase58Check bs++vectors :: [(BS.ByteString, String, String)]+vectors = + [ ( BS.empty, "", "3QJmnh" )+ , ( BS.pack [0], "1", "1Wh4bh" )+ , ( BS.pack [0,0,0,0], "1111", "11114bdQda" )+ , ( BS.pack [0,0,1,0,0], "11LUw", "113CUwsFVuo" )+ , ( BS.pack [255], "5Q", "VrZDWwe" )+ , ( BS.pack [0,0,0,0] `BS.append` BS.pack [1..255]+ , "1111cWB5HCBdLjAuqGGReWE3R3CguuwSjw6RHn39s2yuDRTS5NsBgNiFpWgAnEx6VQi8csexkgYw3mdYrMHr8x9i7aEwP8kZ7vccXWqKDvGv3u1GxFKPuAkn8JCPPGDMf3vMMnbzm6Nh9zh1gcNsMvH3ZNLmP5fSG6DGbbi2tuwMWPthr4boWwCxf7ewSgNQeacyozhKDDQQ1qL5fQFUW52QKUZDZ5fw3KXNQJMcNTcaB723LchjeKun7MuGW5qyCBZYzA1KjofN1gYBV3NqyhQJ3Ns746GNuf9N2pQPmHz4xpnSrrfCvy6TVVz5d4PdrjeshsWQwpZsZGzvbdAdN8MKV5QsBDY"+ , "111151KWPPBRzdWPr1ASeu172gVgLf1YfUp6VJyk6K9t4cLqYtFHcMa2iX8S3NJEprUcW7W5LvaPRpz7UG7puBj5STE3nKhCGt5eckYq7mMn5nT7oTTic2BAX6zDdqrmGCnkszQkzkz8e5QLGDjf7KeQgtEDm4UER6DMSdBjFQVa6cHrrJn9myVyyhUrsVnfUk2WmNFZvkWv3Tnvzo2cJ1xW62XDfUgYz1pd97eUGGPuXvDFfLsBVd1dfdUhPwxW7pMPgdWHTmg5uqKGFF6vE4xXpAqZTbTxRZjCDdTn68c2wrcxApm8hq3JX65Hix7VtcD13FF8b7BzBtwjXq1ze6NMjKgUcqpJTN9vt"+ )+ ]+
tests/Network/Haskoin/Crypto/ECDSA/Tests.hs view
@@ -31,7 +31,7 @@ {- ECDSA Signatures -} -signAndVerify :: Hash256 -> FieldN -> FieldN -> Property+signAndVerify :: Word256 -> FieldN -> FieldN -> Property signAndVerify msg k n = k > 0 && n > 0 ==> case sM of (Just s) -> verifySig msg s (PubKey kP) Nothing -> True -- very bad luck@@ -39,7 +39,7 @@ nP = mulPoint n curveG sM = unsafeSignMsg msg k (n,nP) -signAndVerifyD :: Hash256 -> TestPrvKeyC -> Bool+signAndVerifyD :: Word256 -> TestPrvKeyC -> Bool signAndVerifyD msg (TestPrvKeyC k) = verifySig msg (detSignMsg msg k) p where p = derivePubKey k
tests/Network/Haskoin/Crypto/Hash/Tests.hs view
@@ -4,6 +4,7 @@ import Test.Framework.Providers.QuickCheck2 (testProperty) import Network.Haskoin.Crypto.Hash+import Network.Haskoin.Crypto.BigWord import Network.Haskoin.Crypto.Arbitrary() tests :: [Test]@@ -14,7 +15,7 @@ ] ] -joinSplit512 :: Hash512 -> Bool+joinSplit512 :: Word512 -> Bool joinSplit512 h = (join512 $ split512 h) == h -- After encoding and decoding, we may loose precision so the new result is >=
tests/Network/Haskoin/Crypto/Keys/Tests.hs view
@@ -15,6 +15,7 @@ import Network.Haskoin.Crypto.Point import Network.Haskoin.Crypto.BigWord import Network.Haskoin.Crypto.Curve+import Network.Haskoin.Crypto.ExtendedKeys import Network.Haskoin.Util tests :: [Test]@@ -113,21 +114,21 @@ {- Key properties -} -testAddPubKey :: TestPrvKeyC -> Hash256 -> Bool+testAddPubKey :: TestPrvKeyC -> Word256 -> Bool testAddPubKey (TestPrvKeyC key) i | toInteger i >= curveN = isNothing res | model == InfPoint = isNothing res | otherwise = PubKey model == fromJust res where pub = derivePubKey key- pt = mulPoint (toFieldN i) curveG+ pt = mulPoint (fromIntegral i :: FieldN) curveG model = addPoint (pubKeyPoint pub) pt res = addPubKeys pub i -testAddPrvKey :: TestPrvKeyC -> Hash256 -> Bool+testAddPrvKey :: TestPrvKeyC -> Word256 -> Bool testAddPrvKey (TestPrvKeyC key) i | toInteger i >= curveN = isNothing res | model == 0 = isNothing res | otherwise = PrvKey model == fromJust res- where model = (prvKeyFieldN key) + (toFieldN i)+ where model = (prvKeyFieldN key) + (fromIntegral i :: FieldN) res = addPrvKeys key i
tests/Network/Haskoin/Crypto/Merkle/Tests.hs view
@@ -25,7 +25,7 @@ testBaseWidth :: Int -> Property testBaseWidth i = i > 0 ==> calcTreeWidth i 0 == i -buildExtractTree :: [(Hash256,Bool)] -> Property+buildExtractTree :: [(TxHash,Bool)] -> Property buildExtractTree txs = not (null txs) ==> r == (buildMerkleRoot hashes) && m == (map fst $ filter snd txs) where
tests/Network/Haskoin/Crypto/Merkle/Units.hs view
@@ -5,10 +5,8 @@ import Test.Framework.Providers.HUnit (testCase) import Data.Maybe (fromJust)-import qualified Data.ByteString as BS (reverse) import Network.Haskoin.Crypto-import Network.Haskoin.Util tests :: [Test] tests =@@ -24,9 +22,10 @@ runMerkleVector :: (String,[String]) -> Assertion runMerkleVector (r,hs) = do- assertBool " > Merkle Vector" $ buildMerkleRoot (map f hs) == (f r)+ assertBool " > Merkle Vector" $ + buildMerkleRoot (map f hs) == fromIntegral (f r) where- f = decode' . BS.reverse . fromJust . hexToBS+ f = fromJust . decodeTxHashLE merkleVectors :: [(String,[String])] merkleVectors =
tests/Network/Haskoin/Crypto/Mnemonic/Tests.hs view
@@ -3,7 +3,6 @@ import Data.Bits ((.&.), shiftR) import Data.Binary (Binary)-import qualified Data.Text as T import Data.Word (Word32) import qualified Data.ByteString as BS import Network.Haskoin.Crypto.Arbitrary()@@ -55,25 +54,25 @@ toMnemonic128 x = l == 12 where bs = encode' x- l = length . T.words . fromRight $ toMnemonic english bs+ l = length . words . fromRight $ toMnemonic bs toMnemonic160 :: Word160 -> Bool toMnemonic160 x = l == 15 where bs = encode' x- l = length . T.words . fromRight $ toMnemonic english bs+ l = length . words . fromRight $ toMnemonic bs toMnemonic256 :: Word256 -> Bool toMnemonic256 x = l == 24 where bs = encode' x- l = length . T.words . fromRight $ toMnemonic english bs+ l = length . words . fromRight $ toMnemonic bs toMnemonic512 :: Word512 -> Bool toMnemonic512 x = l == 48 where bs = encode' x- l = length . T.words . fromRight $ toMnemonic english bs+ l = length . words . fromRight $ toMnemonic bs toMnemonicVar :: [Word32] -> Property toMnemonicVar ls = not (length ls > 8) ==> l == wc@@ -82,7 +81,7 @@ bl = BS.length bs cb = bl `div` 4 wc = (cb + bl * 8) `div` 11- l = length . T.words . fromRight $ toMnemonic english bs+ l = length . words . fromRight $ toMnemonic bs {- Encode/Decode -} @@ -90,31 +89,31 @@ fromToMnemonic128 x = bs == bs' where bs = encode' x- bs' = fromRight (fromMnemonic english =<< toMnemonic english bs)+ bs' = fromRight (fromMnemonic =<< toMnemonic bs) fromToMnemonic160 :: Word160 -> Bool fromToMnemonic160 x = bs == bs' where bs = encode' x- bs' = fromRight (fromMnemonic english =<< toMnemonic english bs)+ bs' = fromRight (fromMnemonic =<< toMnemonic bs) fromToMnemonic256 :: Word256 -> Bool fromToMnemonic256 x = bs == bs' where bs = encode' x- bs' = fromRight (fromMnemonic english =<< toMnemonic english bs)+ bs' = fromRight (fromMnemonic =<< toMnemonic bs) fromToMnemonic512 :: Word512 -> Bool fromToMnemonic512 x = bs == bs' where bs = encode' x- bs' = fromRight (fromMnemonic english =<< toMnemonic english bs)+ bs' = fromRight (fromMnemonic =<< toMnemonic bs) fromToMnemonicVar :: [Word32] -> Property fromToMnemonicVar ls = not (length ls > 8) ==> bs == bs' where bs = binWordsToBS ls- bs' = fromRight (fromMnemonic english =<< toMnemonic english bs)+ bs' = fromRight (fromMnemonic =<< toMnemonic bs) {- Mnemonic to seed -} @@ -122,35 +121,35 @@ mnemonicToSeed128 x = l == 64 where bs = encode' x- seed = fromRight (mnemonicToSeed english "" =<< toMnemonic english bs)+ seed = fromRight (mnemonicToSeed "" =<< toMnemonic bs) l = BS.length seed mnemonicToSeed160 :: Word160 -> Bool mnemonicToSeed160 x = l == 64 where bs = encode' x- seed = fromRight (mnemonicToSeed english "" =<< toMnemonic english bs)+ seed = fromRight (mnemonicToSeed "" =<< toMnemonic bs) l = BS.length seed mnemonicToSeed256 :: Word256 -> Bool mnemonicToSeed256 x = l == 64 where bs = encode' x- seed = fromRight (mnemonicToSeed english "" =<< toMnemonic english bs)+ seed = fromRight (mnemonicToSeed "" =<< toMnemonic bs) l = BS.length seed mnemonicToSeed512 :: Word512 -> Bool mnemonicToSeed512 x = l == 64 where bs = encode' x- seed = fromRight (mnemonicToSeed english "" =<< toMnemonic english bs)+ seed = fromRight (mnemonicToSeed "" =<< toMnemonic bs) l = BS.length seed mnemonicToSeedVar :: [Word32] -> Property mnemonicToSeedVar ls = not (length ls > 16) ==> l == 64 where bs = binWordsToBS ls- seed = fromRight (mnemonicToSeed english "" =<< toMnemonic english bs)+ seed = fromRight (mnemonicToSeed "" =<< toMnemonic bs) l = BS.length seed {- Get bits from ByteString -}
tests/Network/Haskoin/Crypto/Mnemonic/Units.hs view
@@ -6,7 +6,6 @@ import Test.Framework.Providers.HUnit (testCase) import Data.Maybe (fromJust)-import qualified Data.Text as T import Network.Haskoin.Crypto.Mnemonic import Network.Haskoin.Util (hexToBS, bsToHex, fromRight) @@ -22,23 +21,23 @@ where f (e, m) = g e . assertEqual "" m . h $ e g = testCase- h = fromRight . toMnemonic english . fromJust . hexToBS+ h = fromRight . toMnemonic . fromJust . hexToBS fromMnemonicTest :: [Test] fromMnemonicTest = map f $ zip ents mss where f (e, m) = g e . assertEqual "" e . h $ m g = testCase- h = bsToHex . fromRight . fromMnemonic english+ h = bsToHex . fromRight . fromMnemonic mnemonicToSeedTest :: [Test] mnemonicToSeedTest = map f $ zip mss seeds where f (m, s) = g s . assertEqual "" s . h $ m g = testCase . (++ "...") . take 50- h = bsToHex . fromRight . mnemonicToSeed english "TREZOR"- + h = bsToHex . fromRight . mnemonicToSeed "TREZOR" + ents :: [String] ents = [ "00000000000000000000000000000000"@@ -67,7 +66,7 @@ , "15da872c95a13dd738fbf50e427583ad61f18fd99f628c417a61cf8343c90419" ] -mss :: [T.Text]+mss :: [Mnemonic] mss = [ "abandon abandon abandon abandon abandon abandon abandon abandon abandon\ \ abandon abandon about"
tests/Network/Haskoin/Crypto/Units.hs view
@@ -169,7 +169,7 @@ assertBool "Key 1C" $ addr1C == (addrToBase58 $ pubKeyAddr pub1C) assertBool "Key 2C" $ addr2C == (addrToBase58 $ pubKeyAddr pub2C) -checkSignatures :: Hash256 -> Assertion+checkSignatures :: Word256 -> Assertion checkSignatures h = do (sign1, sign2, sign1C, sign2C) <- liftIO $ withSource devURandom $ do a <- signMsg h sec1@@ -258,7 +258,7 @@ where sig@(Signature r s) = detSignMsg msg' prv' msg' = hash256 $ stringToBS msg prv' = fromJust $ makePrvKey prv- res = runPut' $ put (fromIntegral r :: Hash256) >> - put (fromIntegral s :: Hash256)+ res = runPut' $ put (fromIntegral r :: Word256) >> + put (fromIntegral s :: Word256)
+ tests/Network/Haskoin/Json/Tests.hs view
@@ -0,0 +1,36 @@+module Network.Haskoin.Json.Tests (tests) where++import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)++import Data.Aeson++import Network.Haskoin.Crypto+import Network.Haskoin.Script+import Network.Haskoin.Protocol+import Network.Haskoin.Transaction+import Network.Haskoin.Crypto.Arbitrary()+import Network.Haskoin.Script.Arbitrary()+import Network.Haskoin.Protocol.Arbitrary()+import Network.Haskoin.Transaction.Arbitrary()+++tests :: [Test]+tests = + [ testGroup "Serialize & de-serialize haskoin types to JSON"+ [ testProperty "Coin" (metaID :: Coin -> Bool)+ , testProperty "ScriptOutput" (metaID :: ScriptOutput -> Bool)+ , testProperty "OutPoint" (metaID :: OutPoint -> Bool)+ , testProperty "Address" (metaID :: Address -> Bool)+ , testProperty "Tx" (metaID :: Tx -> Bool)+ , testProperty "TxHash" (metaID :: TxHash -> Bool)+ , testProperty "Word256" (metaID :: Word256 -> Bool)+ , testProperty "SigHash" (metaID :: SigHash -> Bool)+ , testProperty "SigInput" (metaID :: SigInput -> Bool)+ , testProperty "XPrvKey" (metaID :: XPrvKey -> Bool)+ , testProperty "XPubKey" (metaID :: XPubKey -> Bool)+ ]+ ]++metaID :: (FromJSON a, ToJSON a, Eq a) => a -> Bool+metaID x = (decode . encode) [x] == Just [x]
tests/Network/Haskoin/Protocol/Arbitrary.hs view
@@ -5,7 +5,6 @@ module Network.Haskoin.Protocol.Arbitrary () where import Test.QuickCheck-import Network.Haskoin.Util.Arbitrary (nonEmptyBS) import Network.Haskoin.Crypto.Arbitrary() import Control.Monad@@ -55,19 +54,28 @@ instance Arbitrary Alert where arbitrary = Alert <$> arbitrary <*> arbitrary +instance Arbitrary Reject where+ arbitrary = Reject <$> arbitrary <*> arbitrary <*> arbitrary++instance Arbitrary RejectCode where+ arbitrary = elements [ RejectMalformed+ , RejectInvalid+ , RejectInvalid+ , RejectDuplicate+ , RejectNonStandard+ , RejectDust+ , RejectInsufficientFee+ , RejectCheckpoint+ ]+ instance Arbitrary BlockHeader where arbitrary = BlockHeader <$> arbitrary- <*> (hash256 <$> arbitrary)+ <*> (fromIntegral . hash256 <$> arbitrary) <*> (hash256 <$> arbitrary) <*> arbitrary <*> arbitrary <*> arbitrary -instance Arbitrary Script where- arbitrary = do- i <- choose (1,10)- Script <$> (vectorOf i arbitrary)- instance Arbitrary Tx where arbitrary = do v <- arbitrary@@ -119,29 +127,6 @@ flags <- vectorOf (c*8) arbitrary return $ MerkleBlock h ntx hashes flags -instance Arbitrary PushDataType where- arbitrary = elements [ OPCODE, OPDATA1, OPDATA2, OPDATA4 ]--instance Arbitrary ScriptOp where- arbitrary = oneof [ opPushData <$> nonEmptyBS- , return OP_0- , return OP_1NEGATE- , return OP_1- , return OP_2, return OP_3, return OP_4, return OP_5- , return OP_6, return OP_7, return OP_8, return OP_9- , return OP_10, return OP_11, return OP_12, return OP_13- , return OP_14, return OP_15, return OP_16- , return OP_VERIFY- , return OP_DUP- , return OP_EQUAL- , return OP_EQUALVERIFY- , return OP_HASH160- , return OP_CHECKSIG- , return OP_CHECKMULTISIG- , OP_PUBKEY <$> arbitrary- , return $ OP_INVALIDOPCODE 0xff- ]- instance Arbitrary GetBlocks where arbitrary = GetBlocks <$> arbitrary <*> (listOf arbitrary)@@ -178,6 +163,7 @@ , MCGetHeaders , MCTx , MCBlock+ , MCMerkleBlock , MCHeaders , MCGetAddr , MCFilterLoad@@ -205,6 +191,7 @@ , MGetHeaders <$> arbitrary , MTx <$> arbitrary , MBlock <$> arbitrary+ , MMerkleBlock <$> arbitrary , MHeaders <$> arbitrary , return MGetAddr , MFilterLoad <$> arbitrary@@ -223,8 +210,6 @@ instance Arbitrary BloomFilter where arbitrary = BloomFilter <$> (S.fromList <$> arbitrary)- <*> (return False)- <*> (return False) <*> arbitrary <*> arbitrary <*> arbitrary
tests/Network/Haskoin/Protocol/Tests.hs view
@@ -24,11 +24,10 @@ , testProperty "Version" (metaGetPut :: Version -> Bool) , testProperty "Addr" (metaGetPut :: Addr -> Bool) , testProperty "Alert" (metaGetPut :: Alert -> Bool)+ , testProperty "Reject" (metaGetPut :: Reject -> Bool) , testProperty "TxIn" (metaGetPut :: TxIn -> Bool) , testProperty "TxOut" (metaGetPut :: TxOut -> Bool) , testProperty "OutPoint" (metaGetPut :: OutPoint -> Bool)- , testProperty "ScriptOp" (metaGetPut :: ScriptOp -> Bool)- , testProperty "Script" (metaGetPut :: Script -> Bool) , testProperty "Tx" (metaGetPut :: Tx -> Bool) , testProperty "CoinbaseTx" (metaGetPut :: CoinbaseTx -> Bool) , testProperty "BlockHeader" (metaGetPut :: BlockHeader -> Bool)@@ -49,11 +48,15 @@ ] , testGroup "Transactions" [ testProperty "decode . encode Txid" decEncTxid ]+ , testGroup "Blocks"+ [ testProperty "decode . encode BlockHash id" decEncBlockHashid ] ] metaGetPut :: (Binary a, Eq a) => a -> Bool metaGetPut x = (runGet get (runPut $ put x)) == x -decEncTxid :: Hash256 -> Bool-decEncTxid h = (fromJust $ decodeTxid $ encodeTxid h) == h+decEncTxid :: TxHash -> Bool+decEncTxid h = (fromJust $ decodeTxHashLE $ encodeTxHashLE h) == h +decEncBlockHashid :: BlockHash -> Bool+decEncBlockHashid h = (fromJust $ decodeBlockHashLE $ encodeBlockHashLE h) == h
tests/Network/Haskoin/Protocol/Units.hs view
@@ -6,7 +6,7 @@ import Data.Maybe -import Network.Haskoin.Protocol+import Network.Haskoin.Crypto import Network.Haskoin.Util tests :: [Test]@@ -22,7 +22,7 @@ runTxIDVec :: (String,String) -> Assertion runTxIDVec (tid,tx) = assertBool "TxID" $ - (encodeTxid $ txid txBS) == tid+ (encodeTxHashLE $ txHash txBS) == tid where txBS = decode' $ fromJust $ hexToBS tx
tests/Network/Haskoin/Script/Arbitrary.hs view
@@ -2,7 +2,8 @@ This module provides arbitrary instances for data types in 'Network.Haskoin.Script'. -}-module Network.Haskoin.Script.Arbitrary where+module Network.Haskoin.Script.Arbitrary (ScriptOpInt(..))+where import Test.QuickCheck ( Gen@@ -18,57 +19,208 @@ import Control.Monad (liftM2) import Control.Applicative ((<$>),(<*>)) -import Data.Bits (testBit, clearBit)+import Data.Bits (testBit, clearBit, (.&.)) import Data.Word (Word8) +import Network.Haskoin.Util.Arbitrary (nonEmptyBS) import Network.Haskoin.Script-import Network.Haskoin.Protocol import Network.Haskoin.Crypto +instance Arbitrary Script where+ arbitrary = do+ i <- choose (1,10)+ Script <$> (vectorOf i arbitrary)++instance Arbitrary ScriptOp where+ arbitrary = oneof [ opPushData <$> nonEmptyBS+ , return $ OP_1NEGATE+ , return $ OP_0+ , return $ OP_1+ , return $ OP_2+ , return $ OP_3+ , return $ OP_4+ , return $ OP_5+ , return $ OP_6+ , return $ OP_7+ , return $ OP_8+ , return $ OP_9+ , return $ OP_10+ , return $ OP_11+ , return $ OP_12+ , return $ OP_13+ , return $ OP_14+ , return $ OP_15+ , return $ OP_16++ -- Flow control+ , return $ OP_NOP+ , return $ OP_IF+ , return $ OP_NOTIF+ , return $ OP_ELSE+ , return $ OP_ENDIF+ , return $ OP_VERIFY+ , return $ OP_RETURN++ -- Stack+ , return $ OP_TOALTSTACK+ , return $ OP_FROMALTSTACK+ , return $ OP_2DROP+ , return $ OP_2DUP+ , return $ OP_3DUP+ , return $ OP_2OVER+ , return $ OP_2ROT+ , return $ OP_2SWAP+ , return $ OP_IFDUP+ , return $ OP_DEPTH+ , return $ OP_DROP+ , return $ OP_DUP+ , return $ OP_NIP+ , return $ OP_OVER+ , return $ OP_PICK+ , return $ OP_ROLL+ , return $ OP_ROT+ , return $ OP_SWAP+ , return $ OP_TUCK++ -- Splice+ , return $ OP_CAT+ , return $ OP_SUBSTR+ , return $ OP_LEFT+ , return $ OP_RIGHT+ , return $ OP_SIZE++ -- Bitwise logic+ , return $ OP_INVERT+ , return $ OP_AND+ , return $ OP_OR+ , return $ OP_XOR+ , return $ OP_EQUAL+ , return $ OP_EQUALVERIFY++ -- Arithmetic+ , return $ OP_1ADD+ , return $ OP_1SUB+ , return $ OP_2MUL+ , return $ OP_2DIV+ , return $ OP_NEGATE+ , return $ OP_ABS+ , return $ OP_NOT+ , return $ OP_0NOTEQUAL+ , return $ OP_ADD+ , return $ OP_SUB+ , return $ OP_MUL+ , return $ OP_DIV+ , return $ OP_MOD+ , return $ OP_LSHIFT+ , return $ OP_RSHIFT+ , return $ OP_BOOLAND+ , return $ OP_BOOLOR+ , return $ OP_NUMEQUAL+ , return $ OP_NUMEQUALVERIFY+ , return $ OP_NUMNOTEQUAL+ , return $ OP_LESSTHAN+ , return $ OP_GREATERTHAN+ , return $ OP_LESSTHANOREQUAL+ , return $ OP_GREATERTHANOREQUAL+ , return $ OP_MIN+ , return $ OP_MAX+ , return $ OP_WITHIN++ -- Crypto+ , return $ OP_RIPEMD160+ , return $ OP_SHA1+ , return $ OP_SHA256+ , return $ OP_HASH160+ , return $ OP_HASH256+ , return $ OP_CODESEPARATOR+ , return $ OP_CHECKSIG+ , return $ OP_CHECKSIGVERIFY+ , return $ OP_CHECKMULTISIG+ , return $ OP_CHECKMULTISIGVERIFY++ -- More NOPs+ , return $ OP_NOP1+ , return $ OP_NOP2+ , return $ OP_NOP3+ , return $ OP_NOP4+ , return $ OP_NOP5+ , return $ OP_NOP6+ , return $ OP_NOP7+ , return $ OP_NOP8+ , return $ OP_NOP9+ , return $ OP_NOP10++ , return $ OP_INVALIDOPCODE 0xff+ ]++instance Arbitrary PushDataType where+ arbitrary = elements [ OPCODE, OPDATA1, OPDATA2, OPDATA4 ]+ instance Arbitrary TxSignature where arbitrary = liftM2 TxSignature arbitrary arbitrary instance Arbitrary SigHash where arbitrary = do- w <- arbitrary :: Gen Word8- let acp = testBit w 7- return $ case clearBit w 7 of- 1 -> SigAll acp- 2 -> SigNone acp- 3 -> SigSingle acp- _ -> SigUnknown acp w+ oneof [ SigAll <$> arbitrary+ , SigNone <$> arbitrary+ , SigSingle <$> arbitrary+ , f+ ]+ where+ f = do+ wGen <- arbitrary :: Gen Word8+ -- Make sure we don't have an unknown which is actually known+ let w = if clearBit wGen 7 <= 3 then wGen .&. 0xfc else wGen+ return $ SigUnknown (testBit w 7) w instance Arbitrary ScriptOutput where- arbitrary = oneof - [ PayPK <$> arbitrary- , (PayPKHash . pubKeyAddr) <$> arbitrary - , genPayMulSig- , (PayScriptHash . scriptAddr) <$> arbitrary- ]+ arbitrary = oneof [ genSimpleOutput+ , genPaySHOutput+ ] +genSimpleOutput :: Gen ScriptOutput+genSimpleOutput = oneof+ [ PayPK <$> arbitrary+ , (PayPKHash . pubKeyAddr) <$> arbitrary + , genPayMulSig =<< choose (1,16)+ ]+ +genPaySHOutput :: Gen ScriptOutput+genPaySHOutput = (PayScriptHash . scriptAddr) <$> genSimpleOutput+ -- | Generate an arbitrary 'ScriptOutput' of value PayMulSig.-genPayMulSig :: Gen ScriptOutput-genPayMulSig = do- n <- choose (1,16)- m <- choose (1,n)+genPayMulSig :: Int -> Gen ScriptOutput+genPayMulSig m = do+ n <- choose (m,16) PayMulSig <$> (vectorOf n arbitrary) <*> (return m) -instance Arbitrary ScriptInput where+instance Arbitrary SimpleInput where arbitrary = oneof [ SpendPK <$> arbitrary , SpendPKHash <$> arbitrary <*> arbitrary- , genSpendMulSig+ , genSpendMulSig =<< choose (1,16) ] --- | Generate an arbitrary 'ScriptInput' of value SpendMulSig.-genSpendMulSig :: Gen ScriptInput-genSpendMulSig = do- m <- choose (1,16)- s <- choose (1,m)- SpendMulSig <$> (vectorOf s arbitrary) <*> (return m)+instance Arbitrary ScriptInput where+ arbitrary = oneof+ [ RegularInput <$> arbitrary+ , genScriptHashInput+ ] -instance Arbitrary ScriptHashInput where- arbitrary = ScriptHashInput <$> arbitrary <*> arbitrary+-- | Generate an arbitrary 'SimpleInput of value SpendMulSig.+genSpendMulSig :: Int -> Gen SimpleInput+genSpendMulSig r = do+ s <- choose (1,r)+ SpendMulSig <$> (vectorOf s arbitrary)++genScriptHashInput :: Gen ScriptInput+genScriptHashInput = do+ inp <- arbitrary :: Gen SimpleInput+ out <- case inp of+ SpendPK _ -> PayPK <$> arbitrary+ SpendPKHash _ _ -> (PayPKHash . pubKeyAddr) <$> arbitrary+ SpendMulSig sigs -> genPayMulSig $ length sigs+ return $ ScriptHashInput inp out -- | Data type for generating an arbitrary 'ScriptOp' with a value in -- [OP_1 .. OP_16]
tests/Network/Haskoin/Script/Tests.hs view
@@ -1,14 +1,31 @@-module Network.Haskoin.Script.Tests (tests) where+module Network.Haskoin.Script.Tests +( tests+, execScriptIO+, testValid+, testInvalid+, runTests+) where import Test.QuickCheck.Property (Property, (==>))-import Test.Framework (Test, testGroup)+import Test.Framework (Test, testGroup, buildTest)+import Test.Framework.Providers.HUnit (testCase) import Test.Framework.Providers.QuickCheck2 (testProperty) +import Test.Framework.Runners.Console (defaultMainWithArgs)++import qualified Test.HUnit as HUnit+ import Control.Applicative ((<$>))+import Control.Monad (when) +import Numeric (readHex)++import qualified Data.Aeson as A (decode) import Data.Bits (setBit, testBit)-import Data.Binary (Word8)-import qualified Data.ByteString as BS +import Text.Read (readMaybe)+import Data.List (isPrefixOf)+import Data.Char (ord)+import qualified Data.ByteString as BS ( singleton , length , tail@@ -16,21 +33,47 @@ , pack ) +import qualified Data.ByteString.Lazy as LBS+ ( pack+ , unpack+ )++import Data.Maybe+ ( catMaybes+ , isNothing+ )++import Data.Binary+ ( Binary+ , Word8+ , encode+ , decode+ , decodeOrFail)++import qualified Data.ByteString.Lazy.Char8 as C (readFile)++import Data.Int (Int64)+ import Network.Haskoin.Protocol.Arbitrary ()-import Network.Haskoin.Script.Arbitrary (ScriptOpInt(..))+import Network.Haskoin.Script.Arbitrary import Network.Haskoin.Script+import Network.Haskoin.Script.Evaluator import Network.Haskoin.Crypto import Network.Haskoin.Protocol import Network.Haskoin.Util + tests :: [Test] tests = - [ testGroup "Script Parser"+ [ testGroup "Script types"+ [ testProperty "ScriptOp" (metaGetPut :: ScriptOp -> Bool)+ , testProperty "Script" (metaGetPut :: Script -> Bool)+ ]+ , testGroup "Script Parser" [ testProperty "decode . encode OP_1 .. OP_16" testScriptOpInt , testProperty "decode . encode ScriptOutput" testScriptOutput , testProperty "decode . encode ScriptInput" testScriptInput- , testProperty "decode . encode ScriptHashInput" testScriptHashInput , testProperty "sorting MultiSig scripts" testSortMulSig ] , testGroup "Script SigHash"@@ -42,8 +85,21 @@ , testProperty "decodeCanonical . encode TxSignature" binTxSigCanonical , testProperty "Testing txSigHash with SigSingle" testSigHashOne ]+ , testGroup "Integer Types"+ [ testProperty "decodeInt . encodeInt Int" testEncodeInt+ , testProperty "decodeBool . encodeBool Bool" testEncodeBool+ ]+ , testFile "Canonical Valid Script Test Cases"+ "tests/data/script_valid.json"+ True+ , testFile "Canonical Invalid Script Test Cases"+ "tests/data/script_invalid.json"+ False ] +metaGetPut :: (Binary a, Eq a) => a -> Bool+metaGetPut x = (decode' . encode') x == x+ {- Script Parser -} testScriptOpInt :: ScriptOpInt -> Bool@@ -55,20 +111,17 @@ testScriptInput :: ScriptInput -> Bool testScriptInput si = (decodeInput $ encodeInput si) == Right si -testScriptHashInput :: ScriptHashInput -> Bool-testScriptHashInput sh = (decodeScriptHash $ encodeScriptHash sh) == Right sh- testSortMulSig :: ScriptOutput -> Bool testSortMulSig out = case out of (PayMulSig _ _) -> check $ sortMulSig out _ -> True- where check (PayMulSig ps _) + where check (PayMulSig ps _) | length ps <= 1 = True | otherwise = snd $ foldl f (head ps,True) $ tail ps check _ = False f (a,t) b | t && encode' a <= encode' b = (b,True) | otherwise = (b,False)- + {- Script SigHash -} testCanonicalSig :: TxSignature -> Bool@@ -113,4 +166,152 @@ then res == (setBit 0 248) else res /= (setBit 0 248) where res = txSigHash tx s (length (txIn tx) - 1) (SigSingle acp)++++{- Script Evaluation Primitives -}++testEncodeInt :: Int64 -> Bool+testEncodeInt i | i > 0x7fffffff = isNothing i'+ | i < -0x7fffffff = isNothing i'+ | otherwise = i' == Just i+ where i' = decodeInt $ encodeInt i++testEncodeBool :: Bool -> Bool+testEncodeBool b = decodeBool (encodeBool b) == b++{- Script Evaluation -}++rejectSignature :: SigCheck+rejectSignature _ _ _ = False++{- Parse tests from bitcoin-qt repository -}++type ParseError = String++parseHex' :: String -> Maybe [Word8]+parseHex' (a:b:xs) = case readHex $ [a, b] :: [(Integer, String)] of+ [(i, "")] -> case parseHex' xs of+ Just ops -> Just $ fromIntegral i:ops+ Nothing -> Nothing+ _ -> Nothing+parseHex' [_] = Nothing+parseHex' [] = Just []++parseScript :: String -> Either ParseError Script+parseScript scriptString =+ do bytes <- LBS.pack <$> parseBytes scriptString+ script <- decodeScript bytes+ when (encode script /= bytes) $+ Left "encode script /= bytes"+ when (decode (encode script) /= script) $+ Left "decode (encode script) /= script"+ return script+ where+ decodeScript bytes = case decodeOrFail bytes of+ Left (_, _, e) -> Left $ "decode error: " ++ e+ Right (_, _, Script s) -> Right $ Script s+ parseBytes :: String -> Either ParseError [Word8]+ parseBytes string = concat <$> mapM parseToken (words string)+ parseToken :: String -> Either ParseError [Word8]+ parseToken tok =+ case alternatives of+ (ops:_) -> Right ops+ _ -> Left $ "unknown token " ++ tok+ where alternatives :: [[Word8]]+ alternatives = catMaybes [ parseHex+ , parseInt+ , parseQuote+ , parseOp+ ]+ parseHex | "0x" `isPrefixOf` tok = parseHex' (drop 2 tok)+ | otherwise = Nothing+ parseInt = fromInt . fromIntegral <$>+ (readMaybe tok :: Maybe Integer)+ parseQuote | tok == "''" = Just [0]+ | (head tok) == '\'' && (last tok) == '\'' =+ Just $ encodeBytes $ opPushData $ BS.pack+ $ map (fromIntegral . ord)+ $ init . tail $ tok+ | otherwise = Nothing+ fromInt :: Int64 -> [Word8]+ fromInt n | n == 0 = [0x00]+ | n == -1 = [0x4f]+ | 1 <= n && n <= 16 = [0x50 + fromIntegral n]+ | otherwise = encodeBytes+ $ opPushData $ BS.pack+ $ encodeInt n+ parseOp = encodeBytes <$> (readMaybe $ "OP_" ++ tok)+ encodeBytes = LBS.unpack . encode++testFile :: String -> String -> Bool -> Test+testFile groupLabel path expected = buildTest $ do+ dat <- C.readFile path+ case (A.decode dat) :: Maybe [[String]] of+ Nothing -> return $+ testCase groupLabel $+ HUnit.assertFailure $ "can't read test file " ++ path+ Just testDefs -> return $ testGroup groupLabel $ map parseTest testDefs++ where parseTest :: [String] -> Test+ parseTest (sig:pubKey:[]) = makeTest "" sig pubKey+ parseTest (sig:pubKey:label:[]) = makeTest label sig pubKey++ parseTest v =+ testCase "can't parse test case" $+ HUnit.assertFailure $ "json element " ++ show v++ makeTest :: String -> String -> String -> Test+ makeTest label sig pubKey =+ testCase label' $ case (parseScript sig, parseScript pubKey) of+ (Left e, _) -> parseError $ "can't parse sig: " +++ show sig ++ " error: " ++ e+ (_, Left e) -> parseError $ "can't parse key: " +++ show pubKey ++ " error: " ++ e+ (Right scriptSig, Right scriptPubKey) ->+ runTest scriptSig scriptPubKey++ where label' = "sig: [" ++ sig ++ "] " +++ " pubKey: [" ++ pubKey ++ "] " +++ (if null label+ then ""+ else " label: " ++ label)++ parseError message = HUnit.assertBool+ ("parse error in valid script: " ++ message)+ (expected == False)++ runTest scriptSig scriptPubKey =+ HUnit.assertBool+ (" eval error: " ++ errorMessage)+ (expected == run evalScript)++ where run f = f scriptSig scriptPubKey rejectSignature+ errorMessage = case run execScript of+ Left e -> show e+ Right _ -> " none"+++-- repl utils++execScriptIO :: String -> String -> IO ()+execScriptIO sig key = case (parseScript sig, parseScript key) of+ (Left e, _) -> print $ "sig parse error: " ++ e+ (_, Left e) -> print $ "key parse error: " ++ e+ (Right scriptSig, Right scriptPubKey) ->+ case execScript scriptSig scriptPubKey rejectSignature of+ Left e -> putStrLn $ "error " ++ show e+ Right p -> do putStrLn $ "successful execution"+ putStrLn $ dumpStack $ runStack p++testValid :: Test+testValid = testFile "Canonical Valid Script Test Cases"+ "tests/data/script_valid.json" True++testInvalid :: Test+testInvalid = testFile "Canonical Valid Script Test Cases"+ "tests/data/script_invalid.json" False++runTests :: [Test] -> IO ()+runTests ts = defaultMainWithArgs ts ["--hide-success"]
tests/Network/Haskoin/Script/Units.hs view
@@ -7,7 +7,6 @@ import Data.Maybe (fromJust) import Network.Haskoin.Script-import Network.Haskoin.Protocol import Network.Haskoin.Crypto import Network.Haskoin.Util @@ -19,6 +18,8 @@ (map notCanonicalVectorsMap $ zip notCanonicalVectors [0..]) , testGroup "Multi Signatures" (map mapMulSigVector $ zip mulSigVectors [0..])+ , testGroup "Signature decoding"+ (map sigDecodeMap $ zip scriptSigSignatures [0..]) ] canonicalVectorsMap :: (String,Int) -> Test.Framework.Test@@ -32,6 +33,12 @@ testCase ("Not canonical Sig " ++ (show i)) func where func = testNotCanonicalSig $ notCanonicalVectors !! i+ +sigDecodeMap :: ( String, Int ) -> Test.Framework.Test +sigDecodeMap (_,i) =+ testCase ( "Signature " ++ ( show i ) ) func+ where+ func = testSigDecode $ scriptSigSignatures !! i testCanonicalSig :: String -> Assertion testCanonicalSig str = @@ -55,8 +62,15 @@ runMulSigVector (a,ops) = assertBool " > MultiSig Vector" $ a == b where - s = Script $ runGet' getScriptOps $ fromJust $ hexToBS ops+ s = decode' $ fromJust $ hexToBS ops b = addrToBase58 $ scriptAddr $ fromRight $ decodeOutput s+ +testSigDecode :: String -> Assertion+testSigDecode str =+ let bs = fromJust $ hexToBS str+ eitherSig = decodeSig bs+ in+ assertBool ( unwords [ "Decode failed:", fromLeft eitherSig ] ) $ isRight eitherSig {- Canonical Signatures -} @@ -98,3 +112,10 @@ ) ] +scriptSigSignatures :: [ String ]+scriptSigSignatures =+ -- Signature in input of txid 1983a69265920c24f89aac81942b1a59f7eb30821a8b3fb258f88882b6336053+ [ "304402205ca6249f43538908151fe67b26d020306c0e59fa206cf9f3ccf641f33357119d02206c82f244d04ac0a48024fb9cc246b66e58598acf206139bdb7b75a2941a2b1e401"+ -- Signature in input of txid fb0a1d8d34fa5537e461ac384bac761125e1bfa7fec286fa72511240fa66864d Strange DER sizes. But in Blockchain+ , "3048022200002b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378db4b14e736602220000334a96676e58b1bb01784cb7c556dd8ce1c220171904da22e18fe1e7d1510db501"+ ]
+ tests/Network/Haskoin/Stratum/Arbitrary.hs view
@@ -0,0 +1,138 @@+{-# LANGUAGE FlexibleInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+-- | Arbitrary instances and data types for use in test suites.+module Network.Haskoin.Stratum.Arbitrary+( -- * Arbitrary Data+ ReqRes(..)+) where++import Control.Applicative+import Data.Aeson.Types hiding (Error)+import Data.Text (Text)+import qualified Data.Text as T+import Network.JsonRpc+import Test.QuickCheck.Arbitrary+import Test.QuickCheck.Gen+import Network.Haskoin.Protocol.Arbitrary ()+import Network.Haskoin.Stratum+import Network.Haskoin.Protocol hiding (Message)++-- | A pair of a request and its corresponding response.+-- Id and version should match.+data ReqRes q r = ReqRes !(Request q) !(Response r)+ deriving (Show, Eq)++instance Arbitrary (ReqRes Value Value) where+ arbitrary = do+ rq <- arbitrary+ rs <- arbitrary+ let rs' = rs { getResId = getReqId rq, getResVer = getReqVer rq }+ return $ ReqRes rq rs'++instance Arbitrary Text where+ arbitrary = T.pack <$> arbitrary++instance Arbitrary Ver where+ arbitrary = elements [V1, V2]++instance (Arbitrary q, ToRequest q) => Arbitrary (Request q) where+ arbitrary = do+ q <- arbitrary+ v <- arbitrary+ let m = requestMethod q+ Request v m q <$> arbitrary++instance (Arbitrary n, ToNotif n) => Arbitrary (Notif n) where+ arbitrary = do+ n <- arbitrary+ v <- arbitrary+ let m = notifMethod n+ return $ Notif v m n++instance Arbitrary r => Arbitrary (Response r) where+ arbitrary = Response <$> arbitrary <*> arbitrary <*> arbitrary++instance Arbitrary ErrorObj where+ arbitrary = ErrorObj <$> arbitrary <*> arbitrary <*> arbitrary+ <*> arbitrary <*> arbitrary++instance ( Arbitrary q, Arbitrary n, Arbitrary r+ , ToRequest q, ToNotif n, ToJSON r )+ => Arbitrary (Message q n r)+ where+ arbitrary = oneof [ MsgRequest <$> arbitrary+ , MsgNotif <$> arbitrary+ , MsgResponse <$> arbitrary+ , MsgError <$> arbitrary ]++instance Arbitrary Id where+ arbitrary = oneof [IdInt <$> arbitrary, IdTxt <$> arbitrary]++instance Arbitrary Value where+ arbitrary = resize 10 $ oneof [val, lsn, objn] where+ val = oneof [ toJSON <$> (arbitrary :: Gen String)+ , toJSON <$> (arbitrary :: Gen Int)+ , toJSON <$> (arbitrary :: Gen Double)+ , toJSON <$> (arbitrary :: Gen Bool) ]+ ls = toJSON <$> listOf val+ obj = object . map (\(t, v) -> (T.pack t, v)) <$> listOf ps+ ps = (,) <$> (arbitrary :: Gen String) <*> oneof [val, ls]+ lsn = toJSON <$> listOf (oneof [ls, obj, val])+ objn = object . map (\(t, v) -> (T.pack t, v)) <$> listOf psn+ psn = (,) <$> (arbitrary :: Gen String) <*> oneof [val, ls, obj]++++--+-- Stratum+--++instance Arbitrary StratumTxInfo where+ arbitrary = StratumTxInfo <$> arbitrary <*> arbitrary++instance Arbitrary StratumCoin where+ arbitrary = do+ h <- arbitrary+ o@(OutPoint i _) <- arbitrary+ let t = StratumTxInfo h i+ StratumCoin o t <$> arbitrary++instance Arbitrary StratumRequest where+ arbitrary = oneof [ StratumReqVersion <$> arbitrary <*> arbitrary+ , StratumReqHistory <$> arbitrary+ , StratumReqBalance <$> arbitrary+ , StratumReqUnspent <$> arbitrary+ , StratumReqTx <$> arbitrary+ , StratumBcastTx <$> arbitrary+ , StratumSubAddr <$> arbitrary ]++instance Arbitrary StratumNotif where+ arbitrary = StratumNotifAddr <$> arbitrary <*> arbitrary++instance Arbitrary StratumResult where+ arbitrary = oneof [ StratumSrvVersion <$> arbitrary+ , StratumAddrHistory <$> arbitrary+ , StratumAddrBalance <$> arbitrary <*> arbitrary+ , StratumAddrUnspent <$> arbitrary+ , StratumAddrStatus <$> arbitrary+ , StratumTx <$> arbitrary+ , StratumBcastId <$> arbitrary ]++instance Arbitrary (ReqRes StratumRequest StratumResult) where+ arbitrary = do+ (q, s) <- oneof+ [ (,) <$> (StratumReqVersion <$> arbitrary <*> arbitrary)+ <*> (StratumSrvVersion <$> arbitrary)+ , (,) <$> (StratumReqHistory <$> arbitrary)+ <*> (StratumAddrHistory <$> arbitrary)+ , (,) <$> (StratumReqBalance <$> arbitrary)+ <*> (StratumAddrBalance <$> arbitrary <*> arbitrary)+ , (,) <$> (StratumReqUnspent <$> arbitrary)+ <*> (StratumAddrUnspent <$> arbitrary)+ , (,) <$> (StratumReqTx <$> arbitrary)+ <*> (StratumTx <$> arbitrary)+ , (,) <$> (StratumBcastTx <$> arbitrary)+ <*> (StratumBcastId <$> arbitrary) ]+ i <- arbitrary+ ver <- arbitrary+ return $ ReqRes (Request ver (requestMethod q) q i) (Response ver s i)
+ tests/Network/Haskoin/Stratum/Tests.hs view
@@ -0,0 +1,37 @@+module Network.Haskoin.Stratum.Tests (tests) where++import Data.Aeson+import Data.Aeson.Types+import Test.Framework+import Test.Framework.Providers.QuickCheck2+import Network.JsonRpc+import Network.Haskoin.Stratum+import Network.Haskoin.Stratum.Arbitrary++tests :: [Test]+tests =+ [ testGroup "Stratum JSON-RPC"+ [ testProperty "Encode/decode requests"+ (reqDecode :: Request StratumRequest -> Bool)+ , testProperty "Encode/decode notifications"+ (notifDecode :: Notif StratumNotif -> Bool)+ , testProperty "Encode/decode responses"+ (resDecode :: ReqRes StratumRequest StratumResult -> Bool)+ ]+ ]++reqDecode :: (Eq a, ToRequest a, ToJSON a, FromRequest a) => Request a -> Bool+reqDecode rq = case parseMaybe parseRequest (toJSON rq) of+ Nothing -> False+ Just rqE -> either (const False) (rq ==) rqE++notifDecode :: (Eq a, ToNotif a, ToJSON a, FromNotif a) => Notif a -> Bool+notifDecode rn = case parseMaybe parseNotif (toJSON rn) of+ Nothing -> False+ Just rnE -> either (const False) (rn ==) rnE++resDecode :: (Eq r, ToJSON r, FromResponse r) => ReqRes q r -> Bool+resDecode (ReqRes rq rs) = case parseMaybe (parseResponse rq) (toJSON rs) of+ Nothing -> False+ Just rsE -> either (const False) (rs ==) rsE+
tests/Network/Haskoin/Stratum/Units.hs view
@@ -1,57 +1,116 @@ {-# LANGUAGE OverloadedStrings #-} module Network.Haskoin.Stratum.Units (tests) where -import Control.Monad (liftM)-import Data.Aeson (decode)-import Data.ByteString.Lazy.Char8 (pack)-import Network.Haskoin.Stratum.Message-import Test.Framework (Test, testGroup, buildTest)-import Test.Framework.Providers.HUnit (testCase)+import Control.Applicative+import Control.Monad+import Data.Aeson+import Data.Aeson.Types+import qualified Data.ByteString.Char8 as B8+import Data.Maybe+import Network.Haskoin.Stratum+import Network.Haskoin.Util+import Network.JsonRpc+import Test.Framework+import Test.Framework.Providers.HUnit import qualified Test.HUnit as HUnit tests :: [Test] tests =- [ testFile "Decode JSON-RPC request"- isRequest "tests/data/requests.json"- , testFile "Decode JSON-RPC notification"- isNotif "tests/data/notifications.json"- , testFile "Decode JSON-RPC response"- isResponse "tests/data/responses.json"- , testFile "Decode JSON-RPC error"- isError "tests/data/errors.json"- , testFile "Decode invalid JSON-RPC"- isInvalid "tests/data/invalid.json"+ [ testGroup "JSON-RPC Static Files"+ [ testSingle "Decode JSON-RPC request"+ isRequest+ "tests/data/requests.json"+ , testSingle "Decode JSON-RPC notification"+ isNotif+ "tests/data/notifications.json"+ , testSingle "Decode invalid JSON-RPC request or notification"+ (\x -> not (isRequest x) && not (isNotif x))+ "tests/data/invalid.json"+ , testPair "Decode JSON-RPC response"+ isResponse+ "tests/data/requests.json"+ "tests/data/responses.json"+ , testPair "Decode JSON-RPC error"+ isError+ "tests/data/requests.json"+ "tests/data/errors.json"+ , testPair "Decode invalid JSON-RPC response"+ (\r x -> not (isResponse r x) && not (isError r x))+ "tests/data/requests.json"+ "tests/data/invalid.json"+ , testPair "Decode invalid JSON-RPC error"+ (\r x -> not (isResponse r x) && not (isError r x))+ "tests/data/requests.json"+ "tests/data/invalid.json"+ ] ] -testFile :: String -> (Maybe MessageValue -> Bool) -> String -> Test-testFile label f file = buildTest $ do+testSingle :: String -> (Maybe Value -> Bool) -> String -> Test+testSingle label f file = buildTest $ do vectors <- liftM lines $ readFile file- let test = g vectors- return test+ return $ testGroup label $ do+ (vS, c) <- zip vectors ([1..] :: [Int])+ let t = f $ decodeStrict' (B8.pack vS)+ l = label ++ " " ++ show c+ return $ testCase l $ HUnit.assertBool (fl vS) t where- g vectors = testGroup label $ do- (vector, count) <- zip vectors [0..]- let msg = decode $ pack vector- lbl = label ++ " " ++ show (count :: Int)- return . testCase lbl . HUnit.assertBool (failure vector) $ f msg- failure vector = "Failed to decode: " ++ vector+ fl = ("Failed to decode: " ++) -isRequest :: Maybe MessageValue -> Bool-isRequest (Just (MsgRequest (Request _ _ (Just _)))) = True-isRequest _ = False+testPair :: String -> (Maybe (Request StratumRequest) -> Maybe Value -> Bool)+ -> String -> String -> Test+testPair label f rF vF = buildTest $ do+ rs <- liftM lines $ readFile rF+ vs <- liftM lines $ readFile vF+ return $ testGroup label $ do+ (rS, vS, count) <- zip3 rs vs ([1..] :: [Int])+ let r = p rS+ t = f r (decodeStrict' $ B8.pack vS)+ l = label ++ " " ++ show count+ return $ testCase l $ HUnit.assertBool (fl vS) t+ where+ p :: String -> Maybe (Request StratumRequest)+ p s = fromRight <$> (parseMaybe parseRequest =<< decodeStrict' (B8.pack s))+ fl = ("Failed to decode: " ++) -isNotif :: Maybe MessageValue -> Bool-isNotif (Just (MsgRequest (Request _ _ Nothing))) = True-isNotif _ = False+isRequest :: Maybe Value -> Bool+isRequest vM = case vM of+ Nothing -> False+ Just v -> case f v of+ Error _ -> False+ Success (Right _) -> True+ Success _ -> False+ where+ f :: Value -> Result (Either ErrorObj (Request StratumRequest))+ f v = parse parseRequest v -isResponse :: Maybe MessageValue -> Bool-isResponse (Just (MsgResponse (Response (Right _) _))) = True-isResponse _ = False+isNotif :: Maybe Value -> Bool+isNotif vM = case vM of+ Nothing -> False+ Just v -> case f v of+ Error _ -> False+ Success (Right _) -> True+ Success _ -> False+ where+ f :: Value -> Result (Either ErrorObj (Notif StratumNotif))+ f v = parse parseNotif v -isError :: Maybe MessageValue -> Bool-isError (Just (MsgResponse (Response (Left _) _))) = True-isError _ = False+isResponse :: Maybe (Request StratumRequest) -> Maybe Value -> Bool+isResponse rM vM = fromMaybe False $ do+ r <- rM+ v <- vM+ let resR :: Result (Either ErrorObj (Response StratumResult))+ resR = parse (parseResponse r) v+ return $ case resR of+ Success (Right _) -> True+ _ -> False -isInvalid :: Maybe MessageValue -> Bool-isInvalid Nothing = True-isInvalid _ = False+isError :: Maybe (Request StratumRequest) -> Maybe Value -> Bool+isError rM vM = fromMaybe False $ do+ r <- rM+ v <- vM+ let resR :: Result (Either ErrorObj (Response StratumResult))+ resR = parse (parseResponse r) v+ return $ case resR of+ Success (Left _) -> True+ _ -> False+
tests/Network/Haskoin/Transaction/Arbitrary.hs view
@@ -3,10 +3,10 @@ -} module Network.Haskoin.Transaction.Arbitrary ( genPubKeyC-, genMulSigInput-, genRegularInput +, genMulSigP2SH+, genSpendAddrInput , genAddrOutput-, RegularTx(..)+, SpendAddrTx(..) , MSParam(..) , PKHashSigTemplate(..) , MulSigTemplate(..)@@ -21,7 +21,6 @@ , choose ) -import Control.Monad (liftM) import Control.Applicative ((<$>),(<*>)) import Data.Word (Word64)@@ -46,33 +45,30 @@ m <- choose (1,n) return $ MSParam m n --- | Data type for generating arbitrary transaction with inputs and outputs--- consisting only of script hash or pub key hash scripts.-data RegularTx = RegularTx Tx deriving (Eq, Show)- -- | Generate an arbitrary compressed public key. genPubKeyC :: Gen PubKey genPubKeyC = derivePubKey <$> genPrvKeyC -- | Generate an arbitrary script hash input spending a multisignature -- pay to script hash.-genMulSigInput :: Gen ScriptHashInput-genMulSigInput = do+genMulSigP2SH :: Gen ScriptInput+genMulSigP2SH = do (MSParam m n) <- arbitrary rdm <- PayMulSig <$> (vectorOf n genPubKeyC) <*> (return m)- inp <- SpendMulSig <$> (vectorOf m arbitrary) <*> (return m)+ inp <- SpendMulSig <$> (vectorOf m arbitrary) return $ ScriptHashInput inp rdm -- | Generate an arbitrary transaction input spending a public key hash or -- script hash output.-genRegularInput :: Gen TxIn-genRegularInput = do+genSpendAddrInput :: Gen TxIn+genSpendAddrInput = do op <- arbitrary sq <- arbitrary- sc <- oneof [ encodeScriptHash <$> genMulSigInput- , encodeInput <$> (SpendPKHash <$> arbitrary <*> genPubKeyC)- ]- return $ TxIn op sc sq+ si <- oneof + [ RegularInput <$> (SpendPKHash <$> arbitrary <*> genPubKeyC)+ , genMulSigP2SH+ ]+ return $ TxIn op (encodeInputBS si) sq -- | Generate an arbitrary output paying to a public key hash or script hash -- address.@@ -82,19 +78,29 @@ sc <- oneof [ (PayPKHash . pubKeyAddr) <$> arbitrary , (PayScriptHash . scriptAddr) <$> arbitrary ]- return $ TxOut v $ encodeOutput sc+ return $ TxOut v $ encodeOutputBS sc -instance Arbitrary RegularTx where+-- | Data type for generating arbitrary transaction with inputs and outputs+-- consisting only of script hash or pub key hash scripts.+data SpendAddrTx = SpendAddrTx Tx deriving (Eq, Show)++instance Arbitrary SpendAddrTx where arbitrary = do- x <- choose (1,10)- y <- choose (1,10)- liftM RegularTx $ Tx <$> arbitrary - <*> (vectorOf x genRegularInput) - <*> (vectorOf y genAddrOutput) - <*> arbitrary+ x <- choose (1,5)+ y <- choose (1,10)+ xs <- vectorOf x genSpendAddrInput+ ys <- vectorOf y genAddrOutput+ let tx = Tx 1 xs ys 0+ return $ SpendAddrTx tx instance Arbitrary Coin where- arbitrary = Coin <$> arbitrary <*> arbitrary <*> arbitrary+ arbitrary = Coin <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary++instance Arbitrary SigInput where+ arbitrary = SigInput <$> arbitrary+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary data PKHashSigTemplate = PKHashSigTemplate Tx [SigInput] [PrvKey] deriving (Eq, Show)@@ -109,8 +115,8 @@ prv <- arbitrary sh <- arbitrary let pub = derivePubKey prv- script = encodeOutput $ PayPKHash $ pubKeyAddr pub- sigi = SigInput script op sh+ script = PayPKHash $ pubKeyAddr pub+ sigi = SigInput script op sh Nothing return (op, sigi, prv) -- Generates private keys that can sign an input using the OutPoint and SigInput@@ -123,12 +129,12 @@ sh <- arbitrary let pub = map derivePubKey prv rdm = PayMulSig pub m- script = encodeOutput $ PayScriptHash $ scriptAddr rdm- sigi = SigInputSH script op (encodeOutput rdm) sh+ script = PayScriptHash $ scriptAddr rdm+ sigi = SigInput script op sh (Just rdm) perPrv = permutations prv !! perm return (op, sigi, take m perPrv) -genPayTo :: Gen (String,Word64)+genPayTo :: Gen (String, Word64) genPayTo = do v <- choose (1,2100000000000000) sc <- oneof [ PubKeyAddress <$> arbitrary@@ -139,8 +145,8 @@ -- Generates data for signing a PKHash transaction instance Arbitrary PKHashSigTemplate where arbitrary = do- inC <- choose (0,5)- outC <- choose (0,10)+ inC <- choose (0,3)+ outC <- choose (0,5) dat <- nubBy (\a b -> fst3 a == fst3 b) <$> vectorOf inC genPKHashData perm <- choose (0,max 0 $ length dat - 1) payTo <- vectorOf outC genPayTo@@ -151,8 +157,8 @@ -- Generates data for signing a P2SH transactions instance Arbitrary MulSigTemplate where arbitrary = do- inC <- choose (0,5)- outC <- choose (0,10)+ inC <- choose (0,2)+ outC <- choose (0,5) dat <- nubBy (\a b -> fst3 a == fst3 b) <$> vectorOf inC genMSData perm <- choose (0,max 0 $ length dat - 1) payTo <- vectorOf outC genPayTo
tests/Network/Haskoin/Transaction/Tests.hs view
@@ -21,6 +21,7 @@ , testProperty "testing chooseCoins function" testChooseCoins , testProperty "testing chooseMSCoins function" testChooseMSCoins ]+ -- TODO: Turn these tests into unit tests as they are very heavy , testGroup "Signing Transactions" [ testProperty "Sign and validate PKHash transactions" testSignTx , testProperty "Sign and validate Multisig transactions" testSignMS@@ -36,79 +37,84 @@ x@(ScriptAddress _) -> Right (PayScriptHash x) == out | otherwise = isLeft tx where tx = buildAddrTx os [(addrToBase58 a,v)]- out = decodeOutput $ scriptOutput $ txOut (fromRight tx) !! 0+ out = decodeOutputBS $ scriptOutput $ txOut (fromRight tx) !! 0 -testGuessSize :: RegularTx -> Bool-testGuessSize (RegularTx tx) =+testGuessSize :: SpendAddrTx -> Bool+testGuessSize (SpendAddrTx tx) = -- We compute an upper bound but it should be close enough to the real size- -- We give 3 bytes of slack on every signature (1 on r and 2 on s)- guess >= len && guess - 3*delta <= len- where delta = pki + (sum $ map fst msi)- guess = guessTxSize pki msi pkout msout- len = BS.length $ encode' tx- rIns = map (decodeInput . scriptInput) $ txIn tx- mIns = map (decodeScriptHash . scriptInput) $ txIn tx- pki = length $ filter (isSpendPKHash . fromRight) $ - filter isRight rIns- msi = concat $ map (shData . fromRight) $ filter isRight mIns+ -- We give 2 bytes of slack on every signature (1 on r and 1 on s)+ guess >= len && guess - 2*delta <= len+ where delta = pki + (sum $ map fst msi)+ guess = guessTxSize pki msi pkout msout+ len = BS.length $ encode' tx+ ins = map f $ txIn tx+ f i = fromRight $ decodeInputBS $ scriptInput i+ pki = length $ filter isSpendPKHash ins+ msi = concat $ map shData ins shData (ScriptHashInput _ (PayMulSig keys r)) = [(r,length keys)] shData _ = []- out = map (fromRight . decodeOutput . scriptOutput) $ txOut tx- pkout = length $ filter isPayPKHash out- msout = length $ filter isPayScriptHash out+ out = map (fromRight . decodeOutputBS . scriptOutput) $ txOut tx+ pkout = length $ filter isPayPKHash out+ msout = length $ filter isPayScriptHash out testChooseCoins :: Word64 -> Word64 -> [Coin] -> Bool testChooseCoins target kbfee xs = case chooseCoins target kbfee xs of Right (chosen,change) ->- let outSum = sum $ map (outValue . coinTxOut) chosen+ let outSum = sum $ map coinValue chosen fee = getFee kbfee (length chosen) in outSum == target + change + fee Left _ -> let fee = getFee kbfee (length xs) in target == 0 || s < target || s < target + fee- where s = sum $ map (outValue . coinTxOut) xs+ where s = sum $ map coinValue xs testChooseMSCoins :: Word64 -> Word64 -> MSParam -> [Coin] -> Bool testChooseMSCoins target kbfee (MSParam m n) xs = case chooseMSCoins target kbfee (m,n) xs of Right (chosen,change) ->- let outSum = sum $ map (outValue . coinTxOut) chosen+ let outSum = sum $ map coinValue chosen fee = getMSFee kbfee (m,n) (length chosen) in outSum == target + change + fee Left _ -> let fee = getMSFee kbfee (m,n) (length xs) in target == 0 || s < target + fee- where s = sum $ map (outValue . coinTxOut) xs+ where s = sum $ map coinValue xs {- Signing Transactions -} testSignTx :: PKHashSigTemplate -> Bool testSignTx (PKHashSigTemplate tx sigi prv)- | null $ txIn tx = isBroken txSig && isBroken txSigP- | otherwise = (not $ verifyTx tx verData)- && isComplete txSig - && verifyTx (runBuild txSig) verData- && isPartial txSigP- && (not $ verifyTx (runBuild txSigP) verData)- && isComplete txSigC- && verifyTx (runBuild txSigC) verData- where txSig = detSignTx tx sigi prv- txSigP = detSignTx tx sigi (tail prv)- txSigC = detSignTx (runBuild txSigP) sigi [head prv]- verData = map (\(SigInput s o _) -> (s,o)) sigi+ | null $ txIn tx = (isLeft res) && (isLeft resP)+ | otherwise = (not $ verifyStdTx tx verData)+ && isRight res && stat+ && verifyStdTx txSig verData+ && isRight resP && (not statP)+ && (not $ verifyStdTx txSigP verData)+ && isRight resC && statC+ && verifyStdTx txSigC verData+ where res = detSignTx tx sigi prv+ (txSig, stat) = fromRight res+ resP = detSignTx tx sigi (tail prv)+ (txSigP, statP) = fromRight resP+ resC = detSignTx txSigP sigi [head prv]+ (txSigC, statC) = fromRight resC+ verData = map (\(SigInput s o _ _) -> (s,o)) sigi testSignMS :: MulSigTemplate -> Bool testSignMS (MulSigTemplate tx sigi prv)- | null $ txIn tx = isBroken txSig && isBroken txSigP- | otherwise = (not $ verifyTx tx verData)- && isComplete txSig - && verifyTx (runBuild txSig) verData- && isPartial txSigP- && (not $ verifyTx (runBuild txSigP) verData)- && isComplete txSigC- && verifyTx (runBuild txSigC) verData- where txSig = detSignTx tx sigi prv- txSigP = detSignTx tx sigi (tail prv)- txSigC = detSignTx (runBuild txSigP) sigi [head prv]- verData = map (\(SigInputSH s o _ _) -> (s,o)) sigi+ | null $ txIn tx = (isLeft res) && (isLeft resP)+ | otherwise = (not $ verifyStdTx tx verData)+ && isRight res && stat+ && verifyStdTx txSig verData+ && isRight resP && (not statP)+ && (not $ verifyStdTx txSigP verData)+ && isRight resC && statC+ && verifyStdTx txSigC verData+ where res = detSignTx tx sigi prv+ (txSig, stat) = fromRight res+ resP = detSignTx tx sigi (tail prv)+ (txSigP, statP) = fromRight resP+ resC = detSignTx txSigP sigi [head prv]+ (txSigC, statC) = fromRight resC+ verData = map (\(SigInput s o _ _) -> (s,o)) sigi
tests/Network/Haskoin/Transaction/Units.hs view
@@ -10,6 +10,8 @@ import qualified Data.ByteString as BS (reverse) import Network.Haskoin.Transaction.Builder+import Network.Haskoin.Script+import Network.Haskoin.Crypto import Network.Haskoin.Protocol import Network.Haskoin.Util @@ -30,7 +32,7 @@ runPKHashVec (xs,ys,res) = assertBool "Build PKHash Tx" $ (bsToHex $ encode' tx) == res where tx = fromRight $ buildAddrTx (map f xs) ys- f (tid,ix) = OutPoint (fromJust $ decodeTxid tid) ix+ f (tid,ix) = OutPoint (fromJust $ decodeTxHashLE tid) ix mapVerifyVec :: (([(String,String,String)],String),Int) @@ -40,14 +42,14 @@ runVerifyVec :: ([(String,String,String)],String) -> Int -> Assertion runVerifyVec (is,bsTx) i = - assertBool name $ verifyTx tx $ map f is+ assertBool name $ verifyStdTx tx $ map f is where name = " > Verify transaction " ++ (show i) tx = decode' (fromJust $ hexToBS bsTx) f (o1,o2,bsScript) = - let ops = runGet' getScriptOps (fromJust $ hexToBS bsScript)- op = OutPoint (decode' $ BS.reverse $ fromJust $ hexToBS o1) + let s = fromRight $ decodeOutputBS $ fromJust $ hexToBS bsScript+ op = OutPoint (decode' $ BS.reverse $ fromJust $ hexToBS o1) (runGet' getWord32le $ fromJust $ hexToBS o2)- in (Script ops,op)+ in (s,op) -- These test vectors have been generated from bitcoind raw transaction api
tests/Network/Haskoin/Util/Arbitrary.hs view
@@ -8,7 +8,6 @@ , Gen , arbitrary , choose- , oneof ) import qualified Data.ByteString as BS ( ByteString@@ -17,10 +16,6 @@ , null ) -import Control.Applicative--import Network.Haskoin.Util.BuildMonad (Build(..))- -- Arbitrary instance for strict ByteStrings instance Arbitrary BS.ByteString where arbitrary = do@@ -33,11 +28,3 @@ nonEmptyBS = do bs <- arbitrary return $ if BS.null bs then BS.pack [0] else bs---- Arbitrary instance for the Build monad-instance Arbitrary a => Arbitrary (Build a) where- arbitrary = oneof [ Complete <$> arbitrary- , Partial <$> arbitrary- , return $ Broken "Arbitrary: Broken"- ]-
tests/Network/Haskoin/Util/Tests.hs view
@@ -28,10 +28,6 @@ "testing matchTemplate with two lists" testMatchTemplateLen , testProperty "Testing either helper functions" testEither ]- , testGroup "Build Monad"- [ testProperty "Build monadic composition" testBuildCompose- , testProperty "Testing guardPartial" testGuardPartial- ] ] {- Various utilities -}@@ -91,25 +87,4 @@ && (not $ isRight e) && (fromLeft e == v) && (eitherToMaybe e == Nothing)--{- Build Monad -}--testBuildCompose :: Build Int -> Build Int -> Bool-testBuildCompose ma mb- | isBroken ma || isBroken mb = isBroken res- | isPartial ma || isPartial mb = isPartial res- | otherwise = isComplete res- where - res = ma >>= (\a -> mb >>= (\b -> return $ a + b ))--testGuardPartial :: Build Int -> Build Int -> Bool-testGuardPartial ma mb- | isBroken ma || isBroken mb = isBroken res- | otherwise = isPartial res- where - res = do- a <- ma- guardPartial False- b <- mb- return $ a + b
tests/data/errors.json view
@@ -1,7 +1,6 @@-{"id": 3, "error": "unknown method:blockchain.foo"}-{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "1"}-{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}-{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}-{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}-{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}-{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "5"}+{"id": 1, "error": "unknown method:blockchain.bad"}+{"id": 2, "error": "'NoneType' object has no attribute '__getitem__': badaddress"}+{"id": 3, "error": "'NoneType' object has no attribute '__getitem__': badaddress"}+{"id": 4, "error": "'NoneType' object has no attribute '__getitem__': badaddress"}+{"id": 5, "error": "{u'message': u\"parameter 1 must be hexadecimal string (not 'badtxid')\", u'code': -8}: [u'badtxid']"}+{"id": 6, "result": "{u'message': u'TX rejected', u'code': -22}"}
tests/data/notifications.json view
@@ -1,7 +1,1 @@-{"method": "handleMessage", "params": ["user1", "we were just talking"], "id": null}-{"method": "handleMessage", "params": ["user3", "sorry, gotta go now, ttyl"], "id": null}-{"method": "userLeft", "params": ["user3"], "id": null}-{"jsonrpc": "2.0", "method": "update", "params": [1,2,3,4,5]}-{"jsonrpc": "2.0", "method": "foobar"}-{"jsonrpc": "2.0", "method": "notify_hello", "params": [7]}-{"jsonrpc": "2.0", "method": "notify_sum", "params": [1,2,4]}+{"params": ["1dice8EMZmqKvrGE4Qc9bUFf9PX3xaYDp", "a0d3dca049edc8671f8e6232606d3bf65ac802fb816eed7f2aeceac05f2e3da1"], "id": null, "method": "blockchain.address.subscribe"}
tests/data/requests.json view
@@ -1,12 +1,7 @@-{ "method": "echo", "params": ["Hello JSON-RPC"], "id": 1}-{"method": "postMessage", "params": ["Hello all!"], "id": 99}-{"method": "postMessage", "params": ["I have a question:"], "id": 101}-{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}-{"jsonrpc": "2.0", "method": "subtract", "params": [23, 42], "id": 2}-{"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3}-{"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "id": 4}-{"jsonrpc": "2.0", "method": "foobar", "id": "1"}-{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"}-{"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"}-{"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"}-{"jsonrpc": "2.0", "method": "get_data", "id": "9"}+{"method": "server.version", "params": ["Haskoin 0.2.1", "0.9"], "id": 1}+{"method": "blockchain.address.get_history", "params": ["13iDewdDa28Bfdg7x5RAig6mSYBY89HXyS"], "id": 2}+{"method": "blockchain.address.get_balance", "params": ["1dice8EMZmqKvrGE4Qc9bUFf9PX3xaYDp"], "id": 3}+{"method": "blockchain.address.listunspent", "params": ["1Cf3kSfKrcWRDeeGfUGZkmWGVk4LYWjAsV"], "id": 4}+{"method": "blockchain.transaction.get", "params": ["286b05c22fbdc2f19c5df8cd3af813bbc4d9306dce6d1b89239d35472be39003"], "id": 5}+{"method": "blockchain.transaction.broadcast", "params": ["01000000112c3e4a268b96f58ff7a3b5ce1028814d8698721a265acfcbc2ff810819289803540000008c4930460221009c3d3552e4bf94367ac5551687edf2baf8b64e38d1331127e85563eca241e596022100ff8ee7587dfbf7e8b8c2b3866b9f810279ec765190509a0117b63aa1baf46ff90141040fae49e3c49f6e0fecc3aaba6392aa64b40bb09503f60ea37aceb42b18e8271dc88b596436d1b88d28fb8d7eb4b0b126bee7d62d471201f71ba49dc32397ee90ffffffff2c3e4a268b96f58ff7a3b5ce1028814d8698721a265acfcbc2ff810819289803590000008b48304502207c5edc0349515f180b9e9fd3cad5733759531bc3c52b14cd9d59b97f8635f3740221008ba14e89972216093f1f3682f39a280fcad9d2f9597599686748a362d4dbc3060141049e84cce53b26e0535c1d33dc9253d04041a4a513a9fee362bbdf2ab50f50f719ec2134f6d184887c7261d998af8ad81985e3b0588e5217f0041ac07eb1471bbcffffffffd5d7029075768a1c38a5d2cbdb3ef354f71e393f5c1224e6bf829b8df58c128b070000008c493046022100c1d02fe4d3f87b7ab61af3bc67b6ab86e4d104856519e76859c8943d81cc310202210089131e9426aeec4a4d955ce24d4d78468054e6cd79a8fd2b87bf827d215551ac0141049e84cce53b26e0535c1d33dc9253d04041a4a513a9fee362bbdf2ab50f50f719ec2134f6d184887c7261d998af8ad81985e3b0588e5217f0041ac07eb1471bbcffffffff7a5e725b887371dc2a5a7c38657777b5f5db4114697958191ba4ef37b65d739c090000008c4930460221009a97ce01064590b137dac55d1a4dfbc1c78f5f82b57fe53f8ef3efec835c058c0221009e9e42a598022bd4a66a27230ff19693611943379df9270dc28032b1c9d68cac0141049e84cce53b26e0535c1d33dc9253d04041a4a513a9fee362bbdf2ab50f50f719ec2134f6d184887c7261d998af8ad81985e3b0588e5217f0041ac07eb1471bbcffffffff2c3e4a268b96f58ff7a3b5ce1028814d8698721a265acfcbc2ff810819289803560000008a473044022032b9ce48141beec65ce54cfd933ed4fa000a2e08c55bb05f63514ffea5fd8f1a0220415bcee96337db3e2137d5939e063311ddcbd0df03e8a5713f3b4e95d9064a79014104f85b35199cd4b83957903364a387a340c52784a3a2573be32a497b3c8b04393f8286ee67fa13d74f44c2d723dbdabbb3e3085616256efecf1a400a5b6dddb137ffffffff8ee874249686dfa9ea72b7bc1caadbb1cc22417df42b48c538642f6f46a5a245050000008b483045022100fc1657847823a7b3f9a365b7a7b5346eb03e6d75f6005c56e016d16424e5813402201e2a0ca9b1ffa4de3a96f807c4936957d4bb8fc59c2e3a49ec60840cb8803864014104f85b35199cd4b83957903364a387a340c52784a3a2573be32a497b3c8b04393f8286ee67fa13d74f44c2d723dbdabbb3e3085616256efecf1a400a5b6dddb137ffffffffb33b03d004ad84c34e43516bf00c9257606839c069aeda4ad61bc8cee04b2c90080000008a473044022035b29efb2b096818ae7b638c57df99852083e842ab097a81ec64b6c0970a3744022061adcdbe5fb6c7109c6f4b4c17fe27447fbe112c05592cf5d2e3640949ce1050014104f85b35199cd4b83957903364a387a340c52784a3a2573be32a497b3c8b04393f8286ee67fa13d74f44c2d723dbdabbb3e3085616256efecf1a400a5b6dddb137ffffffff2c3e4a268b96f58ff7a3b5ce1028814d8698721a265acfcbc2ff810819289803620000008b48304502200cdf798ce425d280af9a268ecd9274d81adbdf9098bd6b1e42464f56388a7dd3022100d7b0268feef6b00a060dd1420a455572dd80debd5be807f7a994bfdcb29d87180141040c5de9ae3c1ac9e414caa428eefa6b5ccdc03342173f4985516ba440a76b2bc4d2f6646e104823eaf982c76acf4a00bf3351ff346e08576e0224db4a5d90429bffffffffd5d7029075768a1c38a5d2cbdb3ef354f71e393f5c1224e6bf829b8df58c128b030000008b48304502204e9f07ee9e8c7338a914605407d775e6df9c61e01827eb0c2dabc10055473069022100f3d4e0ca7fcd8e5822c50087be3ab7ac10a5a3407c755c163dc62a27be529cfe0141040c5de9ae3c1ac9e414caa428eefa6b5ccdc03342173f4985516ba440a76b2bc4d2f6646e104823eaf982c76acf4a00bf3351ff346e08576e0224db4a5d90429bffffffffd5b0cba647df6b55cd5c2f8f7a8be9c559dc84e8d5118c62137739ee8d5063e1080000008b4830450221009c9650f8b2d2073ebe441d3928f6f72cb97c71c5ef2ac772b470ce3871a651e602202000de4b42b5b43a8cbfca39ac07d96d156fc9d8d836698552ff3e9930099ed50141040c5de9ae3c1ac9e414caa428eefa6b5ccdc03342173f4985516ba440a76b2bc4d2f6646e104823eaf982c76acf4a00bf3351ff346e08576e0224db4a5d90429bffffffff14e68ba7e56d0876b7c3e836ddc4e9add04b8621b6b1b27bd648de4ff27784bb000000008b483045022062dcc7768418904ab981ff94f619ceec26980c23e82f7961c386c616f9366e60022100d85998f4f654e20b6ef07a6cde8c1ddee5a0194c03ac837a335fd62a0079c21e014104cfd546186a1df230fb9a2d1b3abc1cdc8caf11a103063fd323ca3078fb5784f68567257fb19b8dff775787f29364695d29f8e67b6dc2286aac5afb5a49cef85affffffffa2d54e2ddad6212ec91fd2ac0a6bd18fea60da6b6999cefb3a044855214535f5020000008b483045022060588ded05f6724467addb84052e0ea97e90d91a6ee46077beea8ceb8a82b5ca0221009417b5e186a19cd6f2dbf185844c3fa1c29c85ab6410d7ed026d2de408e7b9f10141040e5c9c1d6f18f89da23740734fabd3f75dc618507a154e3be879cefe1388efbaecb53802a4ba4d68ae74de44326991269cf48e641bba265ce6a89b25c7c64412ffffffffd5d7029075768a1c38a5d2cbdb3ef354f71e393f5c1224e6bf829b8df58c128b050000008b483045022075dc711ccb1353670ea7c39fcb04804e9274d725674f36a12a01c5fa0288a68d0221009c8b3275b775441c27e5f91d078015eec1f6a8d61a9d6cd89afd5d3aa7653c83014104ee87213a130fb1e5383bfaecae9cd9b26b15c8e29eed645849e225b88f86fb54e5d63cb61bf72dd220bdda25d126c34ae12af148e4aef90f430e3617e990c782ffffffff77bdf6c87ea83b081778e14386eafab756ad2bb12a3d410279d54a89421efeb0750000008b483045022100921de58e737c94933df6d2dd7c39c2291e4be0fd81b33239a96bb25c59897df802204ca963701dc7ffdb92eca7e02411595a55eb27c2484f6be7b9a042564419049d014104ee87213a130fb1e5383bfaecae9cd9b26b15c8e29eed645849e225b88f86fb54e5d63cb61bf72dd220bdda25d126c34ae12af148e4aef90f430e3617e990c782fffffffff3fb51759817723bd21849fd13f30d64a123758268e07b9cc619697cf756a7c1040000008c493046022100a2e161108009ebf79ecf0776955db12d3456ca5768b8a5db1117d1940607b6bd022100fb62245f8faf12e34a2accc538a3546d20e08adcb7d888e8000c9308362e45c401410423677819cb6207aa2a164a14de2f8c8442fd1d9e894298db9df0968c1bef5238abc975e8a3cd181e44fe019447ac0d803645bf40d3cb4b1a489867c602feb1c8ffffffffcc7cf50d711e8d8cb793a3e28da625cd98d98765451b6541f593de6fa97241b7240000008b483045022100ae231bb5028331eae13ea6e23980174fff7b3ab04a7cf09c24fe67d5b8caf0b40220229263575a05e8e49190950c322b3a338f2e7e090f9c6f621f4868f22dedf8e501410423677819cb6207aa2a164a14de2f8c8442fd1d9e894298db9df0968c1bef5238abc975e8a3cd181e44fe019447ac0d803645bf40d3cb4b1a489867c602feb1c8fffffffff7125a8c780ac0fa452b1f8f87de45e75ee4b151e247372bac149ee2d63087220d0000008c493046022100dbaf165c9cab408b46f83565dbdd6d1f785ab07ae318b57f11c4822782206a760221008ef6dfd9b50cdb0d28793097576a7dc08c43eee643fcc78130574dadf4e666a901410423677819cb6207aa2a164a14de2f8c8442fd1d9e894298db9df0968c1bef5238abc975e8a3cd181e44fe019447ac0d803645bf40d3cb4b1a489867c602feb1c8ffffffff0259781900000000001976a91433d9960814cc510bad35a511529b9f9f8b3293f888ac80f0fa02000000001976a914f0961abdb44d2d55fa7309033c6401f315f4a0b388ac00000000"], "id": 6}+{"method": "blockchain.address.subscribe", "params": ["1dice8EMZmqKvrGE4Qc9bUFf9PX3xaYDp"], "id": 7}
tests/data/responses.json view
@@ -1,10 +1,7 @@-{ "result": "Hello JSON-RPC", "error": null, "id": 1}-{"result": 1, "error": null, "id": 99}-{"result": 1, "error": null, "id": 101}-{"jsonrpc": "2.0", "result": 19, "id": 1}-{"jsonrpc": "2.0", "result": -19, "id": 2}-{"jsonrpc": "2.0", "result": 19, "id": 3}-{"jsonrpc": "2.0", "result": 19, "id": 4}-{"jsonrpc": "2.0", "result": 7, "id": "1"}-{"jsonrpc": "2.0", "result": 19, "id": "2"}-{"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"}+{"id": 1, "result": "0.9"}+{"id": 2, "result": [{"tx_hash": "76d8c486bd4241f4a3c4515f77cf2341eedb9d921fff6e94dd0fc52764e67215", "height": 309598}, {"tx_hash": "adb4ea9bd7d6f8a84d971baf87a4eb24ca2d4c336e3c6f5a60972fc65f9d8247", "height": 0}]}+{"id": 3, "result": {"confirmed": 12806651, "unconfirmed": 0}}+{"id": 4, "result": [{"tx_hash": "86fd998e9328125b3af31a1eeebb9480ff72e6aeec83995383b83a1b15d37685", "tx_pos": 0, "value": 15000000000, "height": 309607}]}+{"id": 5, "result": "01000000023d57adc8f76668ce783a23851b07a9bea489b6dcffed5f6e252499a0ac8de803000000008b48304502210093574f15c092c3b005d8a58e586a539b31a27edbb698fee9459bd17be812492c02205426398b8a9703553a70973ef53b3cebd3c7a43fce9008c5d762ed73b23a603a0141048cc0b94178715f03ed3d0bceb368191d0fdd7fc16d806567f6f2c45aecafb8f53e5ef849564072189b9b4f8bfe1564da776567ba359cfb0c05e839bcf65371abffffffff31cbd682f9952eafe1b6cbe8259dd6aea16d9c37483d9ba84a4dd6fd6d28a7b0010000008b48304502202c7fc1cc54ce918f5f59ce501ba5969a0fe194dbaac94f69b73f0349155b37cd022100f4046d420506f02fa4556601e09ccac59887b44e627665b948a61ce1e72b614c01410497144fd7c7cff46afef8a370c3902e1314850a46d736d616735f5ef3585d7c618b298d2e7cfd888a64222b90813a2da6687309bc577b4e546f173be6dc6d308fffffffff0210270000000000001976a91455eac42c91e92387b7f82a6f1365b81accf90f3388acbf6ae700000000001976a914068a5929332d715d0dedba940cf75df2ca3ecbed88ac00000000"}+{"id": 6, "result": "4a228f26e3d12d6683ae2e805de2f231f98bc083a86a09260236b14faa1c4430"}+{"id": 7, "result": "76eb1bffd3ca311e959f264aaba1856269599c57754fc9568cf5b5795a302b73"}
+ tests/data/script_invalid.json view
@@ -0,0 +1,366 @@+[+["", "DEPTH", "Test the test: we should have an empty stack after scriptSig evaluation"],+[" ", "DEPTH", "and multiple spaces should not change that."],+[" ", "DEPTH"],+[" ", "DEPTH"],++["", ""],+["", "NOP"],+["", "NOP DEPTH"],+["NOP", ""],+["NOP", "DEPTH"],+["NOP","NOP"],+["NOP","NOP DEPTH"],++["DEPTH", ""],++["0x4c01","0x01 NOP", "PUSHDATA1 with not enough bytes"],+["0x4d0200ff","0x01 NOP", "PUSHDATA2 with not enough bytes"],+["0x4e03000000ffff","0x01 NOP", "PUSHDATA4 with not enough bytes"],++["1", "IF 0x50 ENDIF 1", "0x50 is reserved"],+["0x52", "0x5f ADD 0x60 EQUAL", "0x51 through 0x60 push 1 through 16 onto stack"],+["0","NOP"],+["1", "IF VER ELSE 1 ENDIF", "VER non-functional"],+["0", "IF VERIF ELSE 1 ENDIF", "VERIF illegal everywhere"],+["0", "IF ELSE 1 ELSE VERIF ENDIF", "VERIF illegal everywhere"],+["0", "IF VERNOTIF ELSE 1 ENDIF", "VERNOTIF illegal everywhere"],+["0", "IF ELSE 1 ELSE VERNOTIF ENDIF", "VERNOTIF illegal everywhere"],++["1 IF", "1 ENDIF", "IF/ENDIF can't span scriptSig/scriptPubKey"],+["1 IF 0 ENDIF", "1 ENDIF"],+["1 ELSE 0 ENDIF", "1"],+["0 NOTIF", "123"],++["0", "DUP IF ENDIF"],+["0", "IF 1 ENDIF"],+["0", "DUP IF ELSE ENDIF"],+["0", "IF 1 ELSE ENDIF"],+["0", "NOTIF ELSE 1 ENDIF"],++["0 1", "IF IF 1 ELSE 0 ENDIF ENDIF"],+["0 0", "IF IF 1 ELSE 0 ENDIF ENDIF"],+["1 0", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"],+["0 1", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"],++["0 0", "NOTIF IF 1 ELSE 0 ENDIF ENDIF"],+["0 1", "NOTIF IF 1 ELSE 0 ENDIF ENDIF"],+["1 1", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"],+["0 0", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"],++["1", "IF RETURN ELSE ELSE 1 ENDIF", "Multiple ELSEs"],+["1", "IF 1 ELSE ELSE RETURN ENDIF"],++["1", "ENDIF", "Malformed IF/ELSE/ENDIF sequence"],+["1", "ELSE ENDIF"],+["1", "ENDIF ELSE"],+["1", "ENDIF ELSE IF"],+["1", "IF ELSE ENDIF ELSE"],+["1", "IF ELSE ENDIF ELSE ENDIF"],+["1", "IF ENDIF ENDIF"],+["1", "IF ELSE ELSE ENDIF ENDIF"],++["1", "RETURN"],+["1", "DUP IF RETURN ENDIF"],++["1", "RETURN 'data'", "canonical prunable txout format"],+["0 IF", "RETURN ENDIF 1", "still prunable because IF/ENDIF can't span scriptSig/scriptPubKey"],++["0", "VERIFY 1"],+["1", "VERIFY"],+["1", "VERIFY 0"],++["1 TOALTSTACK", "FROMALTSTACK 1", "alt stack not shared between sig/pubkey"],++["IFDUP", "DEPTH 0 EQUAL"],+["DROP", "DEPTH 0 EQUAL"],+["DUP", "DEPTH 0 EQUAL"],+["1", "DUP 1 ADD 2 EQUALVERIFY 0 EQUAL"],+["NOP", "NIP"],+["NOP", "1 NIP"],+["NOP", "1 0 NIP"],+["NOP", "OVER 1"],+["1", "OVER"],+["0 1", "OVER DEPTH 3 EQUALVERIFY"],+["19 20 21", "PICK 19 EQUALVERIFY DEPTH 2 EQUAL"],+["NOP", "0 PICK"],+["1", "-1 PICK"],+["19 20 21", "0 PICK 20 EQUALVERIFY DEPTH 3 EQUAL"],+["19 20 21", "1 PICK 21 EQUALVERIFY DEPTH 3 EQUAL"],+["19 20 21", "2 PICK 22 EQUALVERIFY DEPTH 3 EQUAL"],+["NOP", "0 ROLL"],+["1", "-1 ROLL"],+["19 20 21", "0 ROLL 20 EQUALVERIFY DEPTH 2 EQUAL"],+["19 20 21", "1 ROLL 21 EQUALVERIFY DEPTH 2 EQUAL"],+["19 20 21", "2 ROLL 22 EQUALVERIFY DEPTH 2 EQUAL"],+["NOP", "ROT 1"],+["NOP", "1 ROT 1"],+["NOP", "1 2 ROT 1"],+["NOP", "0 1 2 ROT"],+["NOP", "SWAP 1"],+["1", "SWAP 1"],+["0 1", "SWAP 1 EQUALVERIFY"],+["NOP", "TUCK 1"],+["1", "TUCK 1"],+["1 0", "TUCK DEPTH 3 EQUALVERIFY SWAP 2DROP"],+["NOP", "2DUP 1"],+["1", "2DUP 1"],+["NOP", "3DUP 1"],+["1", "3DUP 1"],+["1 2", "3DUP 1"],+["NOP", "2OVER 1"],+["1", "2 3 2OVER 1"],+["NOP", "2SWAP 1"],+["1", "2 3 2SWAP 1"],++["'a' 'b'", "CAT", "CAT disabled"],+["'a' 'b' 0", "IF CAT ELSE 1 ENDIF", "CAT disabled"],+["'abc' 1 1", "SUBSTR", "SUBSTR disabled"],+["'abc' 1 1 0", "IF SUBSTR ELSE 1 ENDIF", "SUBSTR disabled"],+["'abc' 2 0", "IF LEFT ELSE 1 ENDIF", "LEFT disabled"],+["'abc' 2 0", "IF RIGHT ELSE 1 ENDIF", "RIGHT disabled"],++["NOP", "SIZE 1"],++["'abc'", "IF INVERT ELSE 1 ENDIF", "INVERT disabled"],+["1 2 0 IF AND ELSE 1 ENDIF", "NOP", "AND disabled"],+["1 2 0 IF OR ELSE 1 ENDIF", "NOP", "OR disabled"],+["1 2 0 IF XOR ELSE 1 ENDIF", "NOP", "XOR disabled"],+["2 0 IF 2MUL ELSE 1 ENDIF", "NOP", "2MUL disabled"],+["2 0 IF 2DIV ELSE 1 ENDIF", "NOP", "2DIV disabled"],+["2 2 0 IF MUL ELSE 1 ENDIF", "NOP", "MUL disabled"],+["2 2 0 IF DIV ELSE 1 ENDIF", "NOP", "DIV disabled"],+["2 2 0 IF MOD ELSE 1 ENDIF", "NOP", "MOD disabled"],+["2 2 0 IF LSHIFT ELSE 1 ENDIF", "NOP", "LSHIFT disabled"],+["2 2 0 IF RSHIFT ELSE 1 ENDIF", "NOP", "RSHIFT disabled"],++["0 1","EQUAL"],+["1 1 ADD", "0 EQUAL"],+["11 1 ADD 12 SUB", "11 EQUAL"],++["2147483648 0 ADD", "NOP", "arithmetic operands must be in range [-2^31...2^31] "],+["-2147483648 0 ADD", "NOP", "arithmetic operands must be in range [-2^31...2^31] "],+["2147483647 DUP ADD", "4294967294 NUMEQUAL", "NUMEQUAL must be in numeric range"],+["'abcdef' NOT", "0 EQUAL", "NOT is an arithmetic operand"],++["2 DUP MUL", "4 EQUAL", "disabled"],+["2 DUP DIV", "1 EQUAL", "disabled"],+["2 2MUL", "4 EQUAL", "disabled"],+["2 2DIV", "1 EQUAL", "disabled"],+["7 3 MOD", "1 EQUAL", "disabled"],+["2 2 LSHIFT", "8 EQUAL", "disabled"],+["2 1 RSHIFT", "1 EQUAL", "disabled"],++["1","NOP1 NOP2 NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 2 EQUAL"],+["'NOP_1_to_10' NOP1 NOP2 NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_11' EQUAL"],++["0x50","1", "opcode 0x50 is reserved"],+["1", "IF 0xba ELSE 1 ENDIF", "opcodes above NOP10 invalid if executed"],+["1", "IF 0xbb ELSE 1 ENDIF"],+["1", "IF 0xbc ELSE 1 ENDIF"],+["1", "IF 0xbd ELSE 1 ENDIF"],+["1", "IF 0xbe ELSE 1 ENDIF"],+["1", "IF 0xbf ELSE 1 ENDIF"],+["1", "IF 0xc0 ELSE 1 ENDIF"],+["1", "IF 0xc1 ELSE 1 ENDIF"],+["1", "IF 0xc2 ELSE 1 ENDIF"],+["1", "IF 0xc3 ELSE 1 ENDIF"],+["1", "IF 0xc4 ELSE 1 ENDIF"],+["1", "IF 0xc5 ELSE 1 ENDIF"],+["1", "IF 0xc6 ELSE 1 ENDIF"],+["1", "IF 0xc7 ELSE 1 ENDIF"],+["1", "IF 0xc8 ELSE 1 ENDIF"],+["1", "IF 0xc9 ELSE 1 ENDIF"],+["1", "IF 0xca ELSE 1 ENDIF"],+["1", "IF 0xcb ELSE 1 ENDIF"],+["1", "IF 0xcc ELSE 1 ENDIF"],+["1", "IF 0xcd ELSE 1 ENDIF"],+["1", "IF 0xce ELSE 1 ENDIF"],+["1", "IF 0xcf ELSE 1 ENDIF"],+["1", "IF 0xd0 ELSE 1 ENDIF"],+["1", "IF 0xd1 ELSE 1 ENDIF"],+["1", "IF 0xd2 ELSE 1 ENDIF"],+["1", "IF 0xd3 ELSE 1 ENDIF"],+["1", "IF 0xd4 ELSE 1 ENDIF"],+["1", "IF 0xd5 ELSE 1 ENDIF"],+["1", "IF 0xd6 ELSE 1 ENDIF"],+["1", "IF 0xd7 ELSE 1 ENDIF"],+["1", "IF 0xd8 ELSE 1 ENDIF"],+["1", "IF 0xd9 ELSE 1 ENDIF"],+["1", "IF 0xda ELSE 1 ENDIF"],+["1", "IF 0xdb ELSE 1 ENDIF"],+["1", "IF 0xdc ELSE 1 ENDIF"],+["1", "IF 0xdd ELSE 1 ENDIF"],+["1", "IF 0xde ELSE 1 ENDIF"],+["1", "IF 0xdf ELSE 1 ENDIF"],+["1", "IF 0xe0 ELSE 1 ENDIF"],+["1", "IF 0xe1 ELSE 1 ENDIF"],+["1", "IF 0xe2 ELSE 1 ENDIF"],+["1", "IF 0xe3 ELSE 1 ENDIF"],+["1", "IF 0xe4 ELSE 1 ENDIF"],+["1", "IF 0xe5 ELSE 1 ENDIF"],+["1", "IF 0xe6 ELSE 1 ENDIF"],+["1", "IF 0xe7 ELSE 1 ENDIF"],+["1", "IF 0xe8 ELSE 1 ENDIF"],+["1", "IF 0xe9 ELSE 1 ENDIF"],+["1", "IF 0xea ELSE 1 ENDIF"],+["1", "IF 0xeb ELSE 1 ENDIF"],+["1", "IF 0xec ELSE 1 ENDIF"],+["1", "IF 0xed ELSE 1 ENDIF"],+["1", "IF 0xee ELSE 1 ENDIF"],+["1", "IF 0xef ELSE 1 ENDIF"],+["1", "IF 0xf0 ELSE 1 ENDIF"],+["1", "IF 0xf1 ELSE 1 ENDIF"],+["1", "IF 0xf2 ELSE 1 ENDIF"],+["1", "IF 0xf3 ELSE 1 ENDIF"],+["1", "IF 0xf4 ELSE 1 ENDIF"],+["1", "IF 0xf5 ELSE 1 ENDIF"],+["1", "IF 0xf6 ELSE 1 ENDIF"],+["1", "IF 0xf7 ELSE 1 ENDIF"],+["1", "IF 0xf8 ELSE 1 ENDIF"],+["1", "IF 0xf9 ELSE 1 ENDIF"],+["1", "IF 0xfa ELSE 1 ENDIF"],+["1", "IF 0xfb ELSE 1 ENDIF"],+["1", "IF 0xfc ELSE 1 ENDIF"],+["1", "IF 0xfd ELSE 1 ENDIF"],+["1", "IF 0xfe ELSE 1 ENDIF"],+["1", "IF 0xff ELSE 1 ENDIF"],++["1 IF 1 ELSE", "0xff ENDIF", "invalid because scriptSig and scriptPubKey are processed separately"],++["NOP", "RIPEMD160"],+["NOP", "SHA1"],+["NOP", "SHA256"],+["NOP", "HASH160"],+["NOP", "HASH256"],++["NOP",+"'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'",+">520 byte push"],+["0",+"IF 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' ENDIF 1",+">520 byte push in non-executed IF branch"],+["1",+"0x61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161",+">201 opcodes executed. 0x61 is NOP"],+["0",+"IF 0x6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161 ENDIF 1",+">201 opcodes including non-executed IF branch. 0x61 is NOP"],+["1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f",+"1 2 3 4 5 6 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f",+">1,000 stack size (0x6f is 3DUP)"],+["1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f",+"1 TOALTSTACK 2 TOALTSTACK 3 4 5 6 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f",+">1,000 stack+altstack size"],+["NOP",+"0 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f 2DUP 0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161",+"10,001-byte scriptPubKey"],++["NOP1","NOP10"],++["1","VER", "OP_VER is reserved"],+["1","VERIF", "OP_VERIF is reserved"],+["1","VERNOTIF", "OP_VERNOTIF is reserved"],+["1","RESERVED", "OP_RESERVED is reserved"],+["1","RESERVED1", "OP_RESERVED1 is reserved"],+["1","RESERVED2", "OP_RESERVED2 is reserved"],+["1","0xba", "0xba == OP_NOP10 + 1"],++["2147483648", "1ADD 1", "We cannot do math on 5-byte integers"],+["2147483648", "NEGATE 1", "We cannot do math on 5-byte integers"],+["-2147483648", "1ADD 1", "Because we use a sign bit, -2147483648 is also 5 bytes"],+["2147483647", "1ADD 1SUB 1", "We cannot do math on 5-byte integers, even if the result is 4-bytes"],+["2147483648", "1SUB 1", "We cannot do math on 5-byte integers, even if the result is 4-bytes"],++["2147483648 1", "BOOLOR 1", "We cannot do BOOLOR on 5-byte integers (but we can still do IF etc)"],+["2147483648 1", "BOOLAND 1", "We cannot do BOOLAND on 5-byte integers"],++["1", "1 ENDIF", "ENDIF without IF"],+["1", "IF 1", "IF without ENDIF"],+["1 IF 1", "ENDIF", "IFs don't carry over"],++["NOP", "IF 1 ENDIF", "The following tests check the if(stack.size() < N) tests in each opcode"],+["NOP", "NOTIF 1 ENDIF", "They are here to catch copy-and-paste errors"],+["NOP", "VERIFY 1", "Most of them are duplicated elsewhere,"],++["NOP", "TOALTSTACK 1", "but, hey, more is always better, right?"],+["1", "FROMALTSTACK"],+["1", "2DROP 1"],+["1", "2DUP"],+["1 1", "3DUP"],+["1 1 1", "2OVER"],+["1 1 1 1 1", "2ROT"],+["1 1 1", "2SWAP"],+["NOP", "IFDUP 1"],+["NOP", "DROP 1"],+["NOP", "DUP 1"],+["1", "NIP"],+["1", "OVER"],+["1 1 1 3", "PICK"],+["0", "PICK 1"],+["1 1 1 3", "ROLL"],+["0", "ROLL 1"],+["1 1", "ROT"],+["1", "SWAP"],+["1", "TUCK"],++["NOP", "SIZE 1"],++["1", "EQUAL 1"],+["1", "EQUALVERIFY 1"],++["NOP", "1ADD 1"],+["NOP", "1SUB 1"],+["NOP", "NEGATE 1"],+["NOP", "ABS 1"],+["NOP", "NOT 1"],+["NOP", "0NOTEQUAL 1"],++["1", "ADD"],+["1", "SUB"],+["1", "BOOLAND"],+["1", "BOOLOR"],+["1", "NUMEQUAL"],+["1", "NUMEQUALVERIFY 1"],+["1", "NUMNOTEQUAL"],+["1", "LESSTHAN"],+["1", "GREATERTHAN"],+["1", "LESSTHANOREQUAL"],+["1", "GREATERTHANOREQUAL"],+["1", "MIN"],+["1", "MAX"],+["1 1", "WITHIN"],++["NOP", "RIPEMD160 1"],+["NOP", "SHA1 1"],+["NOP", "SHA256 1"],+["NOP", "HASH160 1"],+["NOP", "HASH256 1"],++["",+"0 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG",+"202 CHECKMULTISIGS, fails due to 201 op limit"],++["1",+"0 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY"],++["",+"NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG",+"Fails due to 201 sig op limit"],++["1",+"NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY"],+++["0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21", "21 CHECKMULTISIG 1", "nPubKeys > 20"],+["0 'sig' 1 0", "CHECKMULTISIG 1", "nSigs > nPubKeys"],+++["NOP 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL", "Tests for Script.IsPushOnly()"],+["NOP1 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL"],++["0 0x01 0x50", "HASH160 0x14 0xece424a6bb6ddf4db592c0faed60685047a361b1 EQUAL", "OP_RESERVED in P2SH should fail"],+["0 0x01 VER", "HASH160 0x14 0x0f4d7845db968f2a81b530b6f3c1d6246d4c7e01 EQUAL", "OP_VER in P2SH should fail"],++["0x00", "'00' EQUAL", "Basic OP_0 execution"]+]
+ tests/data/script_valid.json view
@@ -0,0 +1,510 @@+[+["", "DEPTH 0 EQUAL", "Test the test: we should have an empty stack after scriptSig evaluation"],+[" ", "DEPTH 0 EQUAL", "and multiple spaces should not change that."],+[" ", "DEPTH 0 EQUAL"],+[" ", "DEPTH 0 EQUAL"],+["1 2", "2 EQUALVERIFY 1 EQUAL", "Similarly whitespace around and between symbols"],+["1 2", "2 EQUALVERIFY 1 EQUAL"],+[" 1 2", "2 EQUALVERIFY 1 EQUAL"],+["1 2 ", "2 EQUALVERIFY 1 EQUAL"],+[" 1 2 ", "2 EQUALVERIFY 1 EQUAL"],++["1", ""],+["0x02 0x01 0x00", "", "all bytes are significant, not only the last one"],+["0x09 0x00000000 0x00000000 0x10", "", "equals zero when cast to Int64"],++["0x01 0x0b", "11 EQUAL", "push 1 byte"],+["0x02 0x417a", "'Az' EQUAL"],+["0x4b 0x417a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a",+ "'Azzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz' EQUAL", "push 75 bytes"],++["0x4c 0x01 0x07","7 EQUAL", "0x4c is OP_PUSHDATA1"],+["0x4d 0x0100 0x08","8 EQUAL", "0x4d is OP_PUSHDATA2"],+["0x4e 0x01000000 0x09","9 EQUAL", "0x4e is OP_PUSHDATA4"],++["0x4c 0x00","0 EQUAL"],+["0x4d 0x0000","0 EQUAL"],+["0x4e 0x00000000","0 EQUAL"],+["0x4f 1000 ADD","999 EQUAL"],+["0", "IF 0x50 ENDIF 1", "0x50 is reserved (ok if not executed)"],+["0x51", "0x5f ADD 0x60 EQUAL", "0x51 through 0x60 push 1 through 16 onto stack"],+["1","NOP"],+["0", "IF VER ELSE 1 ENDIF", "VER non-functional (ok if not executed)"],+["0", "IF RESERVED RESERVED1 RESERVED2 ELSE 1 ENDIF", "RESERVED ok in un-executed IF"],++["1", "DUP IF ENDIF"],+["1", "IF 1 ENDIF"],+["1", "DUP IF ELSE ENDIF"],+["1", "IF 1 ELSE ENDIF"],+["0", "IF ELSE 1 ENDIF"],++["1 1", "IF IF 1 ELSE 0 ENDIF ENDIF"],+["1 0", "IF IF 1 ELSE 0 ENDIF ENDIF"],+["1 1", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"],+["0 0", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"],++["1 0", "NOTIF IF 1 ELSE 0 ENDIF ENDIF"],+["1 1", "NOTIF IF 1 ELSE 0 ENDIF ENDIF"],+["1 0", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"],+["0 1", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"],++["0", "IF 0 ELSE 1 ELSE 0 ENDIF", "Multiple ELSE's are valid and executed inverts on each ELSE encountered"],+["1", "IF 1 ELSE 0 ELSE ENDIF"],+["1", "IF ELSE 0 ELSE 1 ENDIF"],+["1", "IF 1 ELSE 0 ELSE 1 ENDIF ADD 2 EQUAL"],+["'' 1", "IF SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ENDIF 0x14 0x68ca4fec736264c13b859bac43d5173df6871682 EQUAL"],++["1", "NOTIF 0 ELSE 1 ELSE 0 ENDIF", "Multiple ELSE's are valid and execution inverts on each ELSE encountered"],+["0", "NOTIF 1 ELSE 0 ELSE ENDIF"],+["0", "NOTIF ELSE 0 ELSE 1 ENDIF"],+["0", "NOTIF 1 ELSE 0 ELSE 1 ENDIF ADD 2 EQUAL"],+["'' 0", "NOTIF SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ENDIF 0x14 0x68ca4fec736264c13b859bac43d5173df6871682 EQUAL"],++["0", "IF 1 IF RETURN ELSE RETURN ELSE RETURN ENDIF ELSE 1 IF 1 ELSE RETURN ELSE 1 ENDIF ELSE RETURN ENDIF ADD 2 EQUAL", "Nested ELSE ELSE"],+["1", "NOTIF 0 NOTIF RETURN ELSE RETURN ELSE RETURN ENDIF ELSE 0 NOTIF 1 ELSE RETURN ELSE 1 ENDIF ELSE RETURN ENDIF ADD 2 EQUAL"],++["0", "IF RETURN ENDIF 1", "RETURN only works if executed"],++["1 1", "VERIFY"],+["1 0x05 0x01 0x00 0x00 0x00 0x00", "VERIFY", "values >4 bytes can be cast to boolean"],++["10 0 11 TOALTSTACK DROP FROMALTSTACK", "ADD 21 EQUAL"],+["'gavin_was_here' TOALTSTACK 11 FROMALTSTACK", "'gavin_was_here' EQUALVERIFY 11 EQUAL"],++["0 IFDUP", "DEPTH 1 EQUALVERIFY 0 EQUAL"],+["1 IFDUP", "DEPTH 2 EQUALVERIFY 1 EQUALVERIFY 1 EQUAL"],+["0 DROP", "DEPTH 0 EQUAL"],+["0", "DUP 1 ADD 1 EQUALVERIFY 0 EQUAL"],+["0 1", "NIP"],+["1 0", "OVER DEPTH 3 EQUALVERIFY"],+["22 21 20", "0 PICK 20 EQUALVERIFY DEPTH 3 EQUAL"],+["22 21 20", "1 PICK 21 EQUALVERIFY DEPTH 3 EQUAL"],+["22 21 20", "2 PICK 22 EQUALVERIFY DEPTH 3 EQUAL"],+["22 21 20", "0 ROLL 20 EQUALVERIFY DEPTH 2 EQUAL"],+["22 21 20", "1 ROLL 21 EQUALVERIFY DEPTH 2 EQUAL"],+["22 21 20", "2 ROLL 22 EQUALVERIFY DEPTH 2 EQUAL"],+["22 21 20", "ROT 22 EQUAL"],+["22 21 20", "ROT DROP 20 EQUAL"],+["22 21 20", "ROT DROP DROP 21 EQUAL"],+["22 21 20", "ROT ROT 21 EQUAL"],+["22 21 20", "ROT ROT ROT 20 EQUAL"],+["25 24 23 22 21 20", "2ROT 24 EQUAL"],+["25 24 23 22 21 20", "2ROT DROP 25 EQUAL"],+["25 24 23 22 21 20", "2ROT 2DROP 20 EQUAL"],+["25 24 23 22 21 20", "2ROT 2DROP DROP 21 EQUAL"],+["25 24 23 22 21 20", "2ROT 2DROP 2DROP 22 EQUAL"],+["25 24 23 22 21 20", "2ROT 2DROP 2DROP DROP 23 EQUAL"],+["25 24 23 22 21 20", "2ROT 2ROT 22 EQUAL"],+["25 24 23 22 21 20", "2ROT 2ROT 2ROT 20 EQUAL"],+["1 0", "SWAP 1 EQUALVERIFY 0 EQUAL"],+["0 1", "TUCK DEPTH 3 EQUALVERIFY SWAP 2DROP"],+["13 14", "2DUP ROT EQUALVERIFY EQUAL"],+["-1 0 1 2", "3DUP DEPTH 7 EQUALVERIFY ADD ADD 3 EQUALVERIFY 2DROP 0 EQUALVERIFY"],+["1 2 3 5", "2OVER ADD ADD 8 EQUALVERIFY ADD ADD 6 EQUAL"],+["1 3 5 7", "2SWAP ADD 4 EQUALVERIFY ADD 12 EQUAL"],+["0", "SIZE 0 EQUAL"],+["1", "SIZE 1 EQUAL"],+["127", "SIZE 1 EQUAL"],+["128", "SIZE 2 EQUAL"],+["32767", "SIZE 2 EQUAL"],+["32768", "SIZE 3 EQUAL"],+["8388607", "SIZE 3 EQUAL"],+["8388608", "SIZE 4 EQUAL"],+["2147483647", "SIZE 4 EQUAL"],+["2147483648", "SIZE 5 EQUAL"],+["549755813887", "SIZE 5 EQUAL"],+["549755813888", "SIZE 6 EQUAL"],+["9223372036854775807", "SIZE 8 EQUAL"],+["-1", "SIZE 1 EQUAL"],+["-127", "SIZE 1 EQUAL"],+["-128", "SIZE 2 EQUAL"],+["-32767", "SIZE 2 EQUAL"],+["-32768", "SIZE 3 EQUAL"],+["-8388607", "SIZE 3 EQUAL"],+["-8388608", "SIZE 4 EQUAL"],+["-2147483647", "SIZE 4 EQUAL"],+["-2147483648", "SIZE 5 EQUAL"],+["-549755813887", "SIZE 5 EQUAL"],+["-549755813888", "SIZE 6 EQUAL"],+["-9223372036854775807", "SIZE 8 EQUAL"],+["'abcdefghijklmnopqrstuvwxyz'", "SIZE 26 EQUAL"],++["42", "SIZE 1 EQUALVERIFY 42 EQUAL", "SIZE does not consume argument"],++["2 -2 ADD", "0 EQUAL"],+["2147483647 -2147483647 ADD", "0 EQUAL"],+["-1 -1 ADD", "-2 EQUAL"],++["0 0","EQUAL"],+["1 1 ADD", "2 EQUAL"],+["1 1ADD", "2 EQUAL"],+["111 1SUB", "110 EQUAL"],+["111 1 ADD 12 SUB", "100 EQUAL"],+["0 ABS", "0 EQUAL"],+["16 ABS", "16 EQUAL"],+["-16 ABS", "-16 NEGATE EQUAL"],+["0 NOT", "NOP"],+["1 NOT", "0 EQUAL"],+["11 NOT", "0 EQUAL"],+["0 0NOTEQUAL", "0 EQUAL"],+["1 0NOTEQUAL", "1 EQUAL"],+["111 0NOTEQUAL", "1 EQUAL"],+["-111 0NOTEQUAL", "1 EQUAL"],+["1 1 BOOLAND", "NOP"],+["1 0 BOOLAND", "NOT"],+["0 1 BOOLAND", "NOT"],+["0 0 BOOLAND", "NOT"],+["16 17 BOOLAND", "NOP"],+["1 1 BOOLOR", "NOP"],+["1 0 BOOLOR", "NOP"],+["0 1 BOOLOR", "NOP"],+["0 0 BOOLOR", "NOT"],+["16 17 BOOLOR", "NOP"],+["11 10 1 ADD", "NUMEQUAL"],+["11 10 1 ADD", "NUMEQUALVERIFY 1"],+["11 10 1 ADD", "NUMNOTEQUAL NOT"],+["111 10 1 ADD", "NUMNOTEQUAL"],+["11 10", "LESSTHAN NOT"],+["4 4", "LESSTHAN NOT"],+["10 11", "LESSTHAN"],+["-11 11", "LESSTHAN"],+["-11 -10", "LESSTHAN"],+["11 10", "GREATERTHAN"],+["4 4", "GREATERTHAN NOT"],+["10 11", "GREATERTHAN NOT"],+["-11 11", "GREATERTHAN NOT"],+["-11 -10", "GREATERTHAN NOT"],+["11 10", "LESSTHANOREQUAL NOT"],+["4 4", "LESSTHANOREQUAL"],+["10 11", "LESSTHANOREQUAL"],+["-11 11", "LESSTHANOREQUAL"],+["-11 -10", "LESSTHANOREQUAL"],+["11 10", "GREATERTHANOREQUAL"],+["4 4", "GREATERTHANOREQUAL"],+["10 11", "GREATERTHANOREQUAL NOT"],+["-11 11", "GREATERTHANOREQUAL NOT"],+["-11 -10", "GREATERTHANOREQUAL NOT"],+["1 0 MIN", "0 NUMEQUAL"],+["0 1 MIN", "0 NUMEQUAL"],+["-1 0 MIN", "-1 NUMEQUAL"],+["0 -2147483647 MIN", "-2147483647 NUMEQUAL"],+["2147483647 0 MAX", "2147483647 NUMEQUAL"],+["0 100 MAX", "100 NUMEQUAL"],+["-100 0 MAX", "0 NUMEQUAL"],+["0 -2147483647 MAX", "0 NUMEQUAL"],+["0 0 1", "WITHIN"],+["1 0 1", "WITHIN NOT"],+["0 -2147483647 2147483647", "WITHIN"],+["-1 -100 100", "WITHIN"],+["11 -100 100", "WITHIN"],+["-2147483647 -100 100", "WITHIN NOT"],+["2147483647 -100 100", "WITHIN NOT"],++["2147483647 2147483647 SUB", "0 EQUAL"],+["2147483647 DUP ADD", "4294967294 EQUAL", ">32 bit EQUAL is valid"],+["2147483647 NEGATE DUP ADD", "-4294967294 EQUAL"],++["''", "RIPEMD160 0x14 0x9c1185a5c5e9fc54612808977ee8f548b2258d31 EQUAL"],+["'a'", "RIPEMD160 0x14 0x0bdc9d2d256b3ee9daae347be6f4dc835a467ffe EQUAL"],+["'abcdefghijklmnopqrstuvwxyz'", "RIPEMD160 0x14 0xf71c27109c692c1b56bbdceb5b9d2865b3708dbc EQUAL"],+["''", "SHA1 0x14 0xda39a3ee5e6b4b0d3255bfef95601890afd80709 EQUAL"],+["'a'", "SHA1 0x14 0x86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 EQUAL"],+["'abcdefghijklmnopqrstuvwxyz'", "SHA1 0x14 0x32d10c7b8cf96570ca04ce37f2a19d84240d3a89 EQUAL"],+["''", "SHA256 0x20 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 EQUAL"],+["'a'", "SHA256 0x20 0xca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb EQUAL"],+["'abcdefghijklmnopqrstuvwxyz'", "SHA256 0x20 0x71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73 EQUAL"],+["''", "DUP HASH160 SWAP SHA256 RIPEMD160 EQUAL"],+["''", "DUP HASH256 SWAP SHA256 SHA256 EQUAL"],+["''", "NOP HASH160 0x14 0xb472a266d0bd89c13706a4132ccfb16f7c3b9fcb EQUAL"],+["'a'", "HASH160 NOP 0x14 0x994355199e516ff76c4fa4aab39337b9d84cf12b EQUAL"],+["'abcdefghijklmnopqrstuvwxyz'", "HASH160 0x4c 0x14 0xc286a1af0947f58d1ad787385b1c2c4a976f9e71 EQUAL"],+["''", "HASH256 0x20 0x5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456 EQUAL"],+["'a'", "HASH256 0x20 0xbf5d3affb73efd2ec6c36ad3112dd933efed63c4e1cbffcfa88e2759c144f2d8 EQUAL"],+["'abcdefghijklmnopqrstuvwxyz'", "HASH256 0x4c 0x20 0xca139bc10c2f660da42666f72e89a225936fc60f193c161124a672050c434671 EQUAL"],+++["1","NOP1 NOP2 NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 1 EQUAL"],+["'NOP_1_to_10' NOP1 NOP2 NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_10' EQUAL"],++["0", "IF 0xba ELSE 1 ENDIF", "opcodes above NOP10 invalid if executed"],+["0", "IF 0xbb ELSE 1 ENDIF"],+["0", "IF 0xbc ELSE 1 ENDIF"],+["0", "IF 0xbd ELSE 1 ENDIF"],+["0", "IF 0xbe ELSE 1 ENDIF"],+["0", "IF 0xbf ELSE 1 ENDIF"],+["0", "IF 0xc0 ELSE 1 ENDIF"],+["0", "IF 0xc1 ELSE 1 ENDIF"],+["0", "IF 0xc2 ELSE 1 ENDIF"],+["0", "IF 0xc3 ELSE 1 ENDIF"],+["0", "IF 0xc4 ELSE 1 ENDIF"],+["0", "IF 0xc5 ELSE 1 ENDIF"],+["0", "IF 0xc6 ELSE 1 ENDIF"],+["0", "IF 0xc7 ELSE 1 ENDIF"],+["0", "IF 0xc8 ELSE 1 ENDIF"],+["0", "IF 0xc9 ELSE 1 ENDIF"],+["0", "IF 0xca ELSE 1 ENDIF"],+["0", "IF 0xcb ELSE 1 ENDIF"],+["0", "IF 0xcc ELSE 1 ENDIF"],+["0", "IF 0xcd ELSE 1 ENDIF"],+["0", "IF 0xce ELSE 1 ENDIF"],+["0", "IF 0xcf ELSE 1 ENDIF"],+["0", "IF 0xd0 ELSE 1 ENDIF"],+["0", "IF 0xd1 ELSE 1 ENDIF"],+["0", "IF 0xd2 ELSE 1 ENDIF"],+["0", "IF 0xd3 ELSE 1 ENDIF"],+["0", "IF 0xd4 ELSE 1 ENDIF"],+["0", "IF 0xd5 ELSE 1 ENDIF"],+["0", "IF 0xd6 ELSE 1 ENDIF"],+["0", "IF 0xd7 ELSE 1 ENDIF"],+["0", "IF 0xd8 ELSE 1 ENDIF"],+["0", "IF 0xd9 ELSE 1 ENDIF"],+["0", "IF 0xda ELSE 1 ENDIF"],+["0", "IF 0xdb ELSE 1 ENDIF"],+["0", "IF 0xdc ELSE 1 ENDIF"],+["0", "IF 0xdd ELSE 1 ENDIF"],+["0", "IF 0xde ELSE 1 ENDIF"],+["0", "IF 0xdf ELSE 1 ENDIF"],+["0", "IF 0xe0 ELSE 1 ENDIF"],+["0", "IF 0xe1 ELSE 1 ENDIF"],+["0", "IF 0xe2 ELSE 1 ENDIF"],+["0", "IF 0xe3 ELSE 1 ENDIF"],+["0", "IF 0xe4 ELSE 1 ENDIF"],+["0", "IF 0xe5 ELSE 1 ENDIF"],+["0", "IF 0xe6 ELSE 1 ENDIF"],+["0", "IF 0xe7 ELSE 1 ENDIF"],+["0", "IF 0xe8 ELSE 1 ENDIF"],+["0", "IF 0xe9 ELSE 1 ENDIF"],+["0", "IF 0xea ELSE 1 ENDIF"],+["0", "IF 0xeb ELSE 1 ENDIF"],+["0", "IF 0xec ELSE 1 ENDIF"],+["0", "IF 0xed ELSE 1 ENDIF"],+["0", "IF 0xee ELSE 1 ENDIF"],+["0", "IF 0xef ELSE 1 ENDIF"],+["0", "IF 0xf0 ELSE 1 ENDIF"],+["0", "IF 0xf1 ELSE 1 ENDIF"],+["0", "IF 0xf2 ELSE 1 ENDIF"],+["0", "IF 0xf3 ELSE 1 ENDIF"],+["0", "IF 0xf4 ELSE 1 ENDIF"],+["0", "IF 0xf5 ELSE 1 ENDIF"],+["0", "IF 0xf6 ELSE 1 ENDIF"],+["0", "IF 0xf7 ELSE 1 ENDIF"],+["0", "IF 0xf8 ELSE 1 ENDIF"],+["0", "IF 0xf9 ELSE 1 ENDIF"],+["0", "IF 0xfa ELSE 1 ENDIF"],+["0", "IF 0xfb ELSE 1 ENDIF"],+["0", "IF 0xfc ELSE 1 ENDIF"],+["0", "IF 0xfd ELSE 1 ENDIF"],+["0", "IF 0xfe ELSE 1 ENDIF"],+["0", "IF 0xff ELSE 1 ENDIF"],++["NOP",+"'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'",+"520 byte push"],+["1",+"0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161",+"201 opcodes executed. 0x61 is NOP"],+["1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f",+"1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f",+"1,000 stack size (0x6f is 3DUP)"],+["1 TOALTSTACK 2 TOALTSTACK 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f",+"1 2 3 4 5 6 7 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f",+"1,000 stack size (altstack cleared between scriptSig/scriptPubKey)"],+["'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f",+"'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f 2DUP 0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161",+"Max-size (10,000-byte), max-push(520 bytes), max-opcodes(201), max stack size(1,000 items). 0x6f is 3DUP, 0x61 is NOP"],++["0",+"IF 0x5050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050 ENDIF 1",+">201 opcodes, but RESERVED (0x50) doesn't count towards opcode limit."],++["NOP","1"],++["1", "0x01 0x01 EQUAL", "The following is useful for checking implementations of BN_bn2mpi"],+["127", "0x01 0x7F EQUAL"],+["128", "0x02 0x8000 EQUAL", "Leave room for the sign bit"],+["32767", "0x02 0xFF7F EQUAL"],+["32768", "0x03 0x008000 EQUAL"],+["8388607", "0x03 0xFFFF7F EQUAL"],+["8388608", "0x04 0x00008000 EQUAL"],+["2147483647", "0x04 0xFFFFFF7F EQUAL"],+["2147483648", "0x05 0x0000008000 EQUAL"],+["549755813887", "0x05 0xFFFFFFFF7F EQUAL"],+["549755813888", "0x06 0xFFFFFFFF7F EQUAL"],+["9223372036854775807", "0x08 0xFFFFFFFFFFFFFF7F EQUAL"],+["-1", "0x01 0x81 EQUAL", "Numbers are little-endian with the MSB being a sign bit"],+["-127", "0x01 0xFF EQUAL"],+["-128", "0x02 0x8080 EQUAL"],+["-32767", "0x02 0xFFFF EQUAL"],+["-32768", "0x03 0x008080 EQUAL"],+["-8388607", "0x03 0xFFFFFF EQUAL"],+["-8388608", "0x04 0x00008080 EQUAL"],+["-2147483647", "0x04 0xFFFFFFFF EQUAL"],+["-2147483648", "0x05 0x0000008080 EQUAL"],+["-4294967295", "0x05 0xFFFFFFFF80 EQUAL"],+["-549755813887", "0x05 0xFFFFFFFFFF EQUAL"],+["-549755813888", "0x06 0x000000008080 EQUAL"],+["-9223372036854775807", "0x08 0xFFFFFFFFFFFFFFFF EQUAL"],++["2147483647", "1ADD 2147483648 EQUAL", "We can do math on 4-byte integers, and compare 5-byte ones"],+["2147483647", "1ADD 1"],+["-2147483647", "1ADD 1"],++["1", "0x02 0x0100 EQUAL NOT", "Not the same byte array..."],+["1", "0x02 0x0100 NUMEQUAL", "... but they are numerically equal"],+["11", "0x4c 0x03 0x0b0000 NUMEQUAL"],+["0", "0x01 0x80 EQUAL NOT"],+["0", "0x01 0x80 NUMEQUAL", "Zero numerically equals negative zero"],+["0", "0x02 0x0080 NUMEQUAL"],+["0x03 0x000080", "0x04 0x00000080 NUMEQUAL"],+["0x03 0x100080", "0x04 0x10000080 NUMEQUAL"],+["0x03 0x100000", "0x04 0x10000000 NUMEQUAL"],++["NOP", "NOP 1", "The following tests check the if(stack.size() < N) tests in each opcode"],+["1", "IF 1 ENDIF", "They are here to catch copy-and-paste errors"],+["0", "NOTIF 1 ENDIF", "Most of them are duplicated elsewhere,"],+["1", "VERIFY 1", "but, hey, more is always better, right?"],++["0", "TOALTSTACK 1"],+["1", "TOALTSTACK FROMALTSTACK"],+["0 0", "2DROP 1"],+["0 1", "2DUP"],+["0 0 1", "3DUP"],+["0 1 0 0", "2OVER"],+["0 1 0 0 0 0", "2ROT"],+["0 1 0 0", "2SWAP"],+["1", "IFDUP"],+["NOP", "DEPTH 1"],+["0", "DROP 1"],+["1", "DUP"],+["0 1", "NIP"],+["1 0", "OVER"],+["1 0 0 0 3", "PICK"],+["1 0", "PICK"],+["1 0 0 0 3", "ROLL"],+["1 0", "ROLL"],+["1 0 0", "ROT"],+["1 0", "SWAP"],+["0 1", "TUCK"],++["1", "SIZE"],++["0 0", "EQUAL"],+["0 0", "EQUALVERIFY 1"],++["0", "1ADD"],+["2", "1SUB"],+["-1", "NEGATE"],+["-1", "ABS"],+["0", "NOT"],+["-1", "0NOTEQUAL"],++["1 0", "ADD"],+["1 0", "SUB"],+["-1 -1", "BOOLAND"],+["-1 0", "BOOLOR"],+["0 0", "NUMEQUAL"],+["0 0", "NUMEQUALVERIFY 1"],+["-1 0", "NUMNOTEQUAL"],+["-1 0", "LESSTHAN"],+["1 0", "GREATERTHAN"],+["0 0", "LESSTHANOREQUAL"],+["0 0", "GREATERTHANOREQUAL"],+["-1 0", "MIN"],+["1 0", "MAX"],+["-1 -1 0", "WITHIN"],++["0", "RIPEMD160"],+["0", "SHA1"],+["0", "SHA256"],+["0", "HASH160"],+["0", "HASH256"],+["NOP", "CODESEPARATOR 1"],++["NOP", "NOP1 1"],+["NOP", "NOP2 1"],+["NOP", "NOP3 1"],+["NOP", "NOP4 1"],+["NOP", "NOP5 1"],+["NOP", "NOP6 1"],+["NOP", "NOP7 1"],+["NOP", "NOP8 1"],+["NOP", "NOP9 1"],+["NOP", "NOP10 1"],++["", "0 0 0 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "CHECKMULTISIG is allowed to have zero keys and/or sigs"],+["", "0 0 0 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 0 1 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "Zero sigs means no sigs are checked"],+["", "0 0 0 1 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],++["", "0 0 0 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "CHECKMULTISIG is allowed to have zero keys and/or sigs"],+["", "0 0 0 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 0 1 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "Zero sigs means no sigs are checked"],+["", "0 0 0 1 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],++["", "0 0 'a' 'b' 2 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "Test from up to 20 pubkeys, all not checked"],+["", "0 0 'a' 'b' 'c' 3 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 4 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 5 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 6 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 7 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 8 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 9 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 10 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 11 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 12 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 13 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 14 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 15 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 16 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 17 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 18 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 19 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 1 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 2 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 3 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 4 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 5 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 6 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 7 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 8 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 9 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 10 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 11 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 12 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 13 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 14 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 15 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 16 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 17 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 18 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 19 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],+["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"],++["",+"0 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG",+"nOpCount is incremented by the number of keys evaluated in addition to the usual one op per op. In this case we have zero keys, so we can execute 201 CHECKMULTISIGS"],++["1",+"0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY"],++["",+"NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG",+"Even though there are no signatures being checked nOpCount is incremented by the number of keys."],++["1",+"NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY"],++["0 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL", "Very basic P2SH"],+["0x4c 0 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL"],++["0x40 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242",+"0x4d 0x4000 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242 EQUAL",+"Basic PUSH signedness check"],++["0x4c 0x40 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242",+"0x4d 0x4000 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242 EQUAL",+"Basic PUSHDATA1 signedness check"],++["0x00", "SIZE 0 EQUAL", "Basic OP_0 execution"]+]