bitcoin-hs-0.0.1: Bitcoin/Script/Base.hs
-- | Bitcoin scripts
{-# LANGUAGE PatternGuards #-}
module Bitcoin.Script.Base where
--------------------------------------------------------------------------------
import Data.Int
import Data.Word
import Text.Show
import qualified Data.ByteString as B
import Bitcoin.Misc.OctetStream
import Bitcoin.Misc.HexString
import Bitcoin.Protocol.Hash
import Bitcoin.Script.Integer
--------------------------------------------------------------------------------
-- | A raw script (octet stream)
newtype RawScript = RawScript { fromRawScript :: B.ByteString } deriving Eq
-- | A parsed script (opcode stream)
newtype Script = Script { fromScript :: [Opcode] } deriving Eq
-- | The 160-bit hash of a (raw) script
newtype ScriptHash = ScriptHash { fromScriptHash :: Hash160 } deriving (Eq,Show)
emptyRawScript :: RawScript
emptyRawScript = RawScript (B.empty)
emptyScript :: Script
emptyScript = Script []
scriptHash :: RawScript -> ScriptHash
scriptHash = ScriptHash . doHash160 . fromRawScript
instance OctetStream ScriptHash where
toByteString = toByteString . fromScriptHash
fromByteString = ScriptHash . fromByteString
instance OctetStream RawScript where
toByteString = fromRawScript
fromByteString = RawScript
instance Show RawScript where
showsPrec d (RawScript rs) = showParen (d>10) $ showString "rawScriptFromString " . shows (toHexStringChars rs)
instance Show Script where
showsPrec d (Script opcodes) = showParen (d>10) $ showString "Script " . (showOpcodeList opcodes)
rawScriptFromString :: String -> RawScript
rawScriptFromString s = if odd (length s)
then error "rawScriptFromString: hex string should have even length"
else RawScript $ fromHexString $ HexString s
--------------------------------------------------------------------------------
-- * shorthands
-- | @OP_FALSE@ = @OP_0@ = 0x00 = pushes an empty array to stack.
op_FALSE :: Opcode
op_FALSE = OP_SMALLNUM 0
-- | @OP_TRUE@ = @OP_1@ = 0x51 = pushes the number 1 onto the stack
op_TRUE :: Opcode
op_TRUE = OP_SMALLNUM 1
-- | Pushes the corresponding number to the stack (0 is represented by an empty array)
op_0 :: Opcode
op_0 = OP_SMALLNUM 0
op_1 :: Opcode
op_1 = OP_SMALLNUM 1
op_2 :: Opcode
op_2 = OP_SMALLNUM 2
op_3 :: Opcode
op_3 = OP_SMALLNUM 3
-- | Handy shorthands for comparisons
op_LT :: Opcode
op_LT = OP_LESSTHAN
op_GT :: Opcode
op_GT = OP_GREATERTHAN
op_LE :: Opcode
op_LE = OP_LESSTHANOREQUAL
op_GE :: Opcode
op_GE = OP_GREATERTHANOREQUAL
-- | Figures out which opcode to use
op_PUSHDATA :: B.ByteString -> Opcode
op_PUSHDATA bs = let l = B.length bs in case l of
0 -> OP_SMALLNUM 0 -- OP_0 = push empty array
_ | l>=1 && l<=75 -> OP_PUSHDATA (fromIntegral l) bs
_ | l<=255 -> OP_PUSHDATA 76 bs
_ | l<=65536 -> OP_PUSHDATA 77 bs
_ -> OP_PUSHDATA 78 bs
-- | Pushes a (signed) integer onto the stack,
-- never using OP_SMALLNUM (except for 0, which is represented by the empty array)
op_BIGNUMBER :: Integer -> Opcode
op_BIGNUMBER n = op_PUSHDATA (asByteString n)
--------------------------------------------------------------------------------
-- * opcodes
-- | See <https://en.bitcoin.it/wiki/Script>
data Opcode
-- data
= OP_SMALLNUM !Int -- ^ OP_0, OP_1 .. OP_16 (bytes 0,81,82..96). Pushes the number to the stack (0 is represented by an empty array)
| OP_1NEGATE -- ^ The number -1 is pushed onto the stack.
| OP_PUSHDATA !Word8 !B.ByteString -- ^ Pushes data to the stack. The Word8 is the opcode: it can be 0, 1..75 and 76,77,78.
-- control flow
| OP_NOP !Word8 -- ^ Does nothing. The argument is the opcode, either 0x61 or 0xb0-0xb9
| OP_IF -- ^ If the top stack value is not 0, the statements are executed. The top stack value is removed.
| OP_NOTIF -- ^ If the top stack value is 0, the statements are executed. The top stack value is removed.
| OP_ELSE -- ^ If the preceding OP_IF or OP_NOTIF or OP_ELSE was not executed then these statements are and if the preceding OP_IF or OP_NOTIF or OP_ELSE was executed then these statements are not.
| OP_ENDIF -- ^ Ends an if/else block.
| OP_VERIFY -- ^ Marks transaction as invalid if top stack value is not true. True is removed, but false is not.
| OP_RETURN -- ^ Marks transaction as invalid.
-- stack
| OP_TOALTSTACK -- ^ Puts the input onto the top of the alt stack. Removes it from the main stack.
| OP_FROMALTSTACK -- ^ Puts the input onto the top of the main stack. Removes it from the alt stack.
| OP_IFDUP -- ^ If the top stack value is not 0, duplicate it.
| OP_DEPTH -- ^ Puts the number of stack items onto the stack.
| OP_DROP -- ^ Removes the top stack item.
| OP_DUP -- ^ Duplicates the top stack item.
| OP_NIP -- ^ Removes the second-to-top stack item.
| OP_OVER -- ^ Copies the second-to-top stack item to the top.
| OP_PICK -- ^ The item n back in the stack is copied to the top.
| OP_ROLL -- ^ The item n back in the stack is moved to the top.
| OP_ROT -- ^ The top three items on the stack are rotated to the left.
| OP_SWAP -- ^ The top two items on the stack are swapped.
| OP_TUCK -- ^ The item at the top of the stack is copied and inserted before the second-to-top item.
| OP_2DROP -- ^ Removes the top two stack items.
| OP_2DUP -- ^ Duplicates the top two stack items.
| OP_3DUP -- ^ Duplicates the top three stack items.
| OP_2OVER -- ^ Copies the pair of items two spaces back in the stack to the front.
| OP_2ROT -- ^ The fifth and sixth items back are moved to the top of the stack.
| OP_2SWAP -- ^ Swaps the top two pairs of items.
-- splice
| OP_CAT -- ^ Concatenates two strings. Currently disabled.
| OP_SUBSTR -- ^ Returns a section of a string. Currently disabled.
| OP_LEFT -- ^ Keeps only characters left of the specified point in a string. Currently disabled.
| OP_RIGHT -- ^ Keeps only characters right of the specified point in a string. Currently disabled.
| OP_SIZE -- ^ Returns the length of the input string.
-- bitwise logic
| OP_INVERT -- ^ Flips all of the bits in the input. Currently disabled.
| OP_AND -- ^ Boolean and between each bit in the inputs. Currently disabled.
| OP_OR -- ^ Boolean or between each bit in the inputs. Currently disabled.
| OP_XOR -- ^ Boolean exclusive or between each bit in the inputs. Currently disabled.
| OP_EQUAL -- ^ Returns 1 if the inputs are exactly equal, 0 otherwise.
| OP_EQUALVERIFY -- ^ Same as OP_EQUAL, but runs OP_VERIFY afterward.
-- arithmetic
| OP_1ADD -- ^ 1 is added to the input.
| OP_1SUB -- ^ 1 is subtracted from the input.
| OP_2MUL -- ^ The input is multiplied by 2. Currently disabled.
| OP_2DIV -- ^ The input is divided by 2. Currently disabled.
| OP_NEGATE -- ^ The sign of the input is flipped.
| OP_ABS -- ^ The input is made positive.
| OP_NOT -- ^ If the input is 0 or 1, it is flipped. Otherwise the output will be 0.
| OP_0NOTEQUAL -- ^ Returns 0 if the input is 0. 1 otherwise.
| OP_ADD -- ^ a is added to b.
| OP_SUB -- ^ b is subtracted from a.
| OP_MUL -- ^ a is multiplied by b. Currently disabled.
| OP_DIV -- ^ a is divided by b. Currently disabled.
| OP_MOD -- ^ Returns the remainder after dividing a by b. Currently disabled.
| OP_LSHIFT -- ^ Shifts a left b bits, preserving sign. Currently disabled.
| OP_RSHIFT -- ^ Shifts a right b bits, preserving sign. Currently disabled.
| OP_BOOLAND -- ^ If both a and b are not 0, the output is 1. Otherwise 0.
| OP_BOOLOR -- ^ If a or b is not 0, the output is 1. Otherwise 0.
| OP_NUMEQUAL -- ^ Returns 1 if the numbers are equal, 0 otherwise.
| OP_NUMEQUALVERIFY -- ^ Same as OP_NUMEQUAL, but runs OP_VERIFY afterward.
| OP_NUMNOTEQUAL -- ^ Returns 1 if the numbers are not equal, 0 otherwise.
| OP_LESSTHAN -- ^ Returns 1 if a is less than b, 0 otherwise.
| OP_GREATERTHAN -- ^ Returns 1 if a is greater than b, 0 otherwise.
| OP_LESSTHANOREQUAL -- ^ Returns 1 if a is less than or equal to b, 0 otherwise.
| OP_GREATERTHANOREQUAL -- ^ Returns 1 if a is greater than or equal to b, 0 otherwise.
| OP_MIN -- ^ Returns the smaller of a and b.
| OP_MAX -- ^ Returns the larger of a and b.
| OP_WITHIN -- ^ Returns 1 if x is within the specified range (left-inclusive), 0 otherwise
-- crypto
| OP_RIPEMD160 -- ^ The input is hashed using RIPEMD-160.
| OP_SHA1 -- ^ The input is hashed using SHA-1.
| OP_SHA256 -- ^ The input is hashed using SHA-256.
| OP_HASH160 -- ^ The input is hashed twice: first with SHA-256 and then with RIPEMD-160.
| OP_HASH256 -- ^ The input is hashed two times with SHA-256.
| OP_CODESEPARATOR -- ^ All of the signature checking words will only match signatures to the data after the most recently-executed OP_CODESEPARATOR.
| OP_CHECKSIG -- ^ The entire transaction's outputs, inputs, and script (from the most recently-executed OP_CODESEPARATOR to the end) are hashed. The signature used by OP_CHECKSIG must be a valid signature for this hash and public key. If it is, 1 is returned, 0 otherwise.
| OP_CHECKSIGVERIFY -- ^ Same as OP_CHECKSIG, but OP_VERIFY is executed afterward.
| OP_CHECKMULTISIG -- ^ For each signature and public key pair, OP_CHECKSIG is executed. If more public keys than signatures are listed, some key/sig pairs can fail. All signatures need to match a public key. If all signatures are valid, 1 is returned, 0 otherwise. Due to a bug, one extra unused value is removed from the stack.
| OP_CHECKMULTISIGVERIFY -- ^ Same as OP_CHECKMULTISIG, but OP_VERIFY is executed afterward.
-- reserved words
| OP_RESERVED -- ^ Transaction is invalid unless occuring in an unexecuted OP_IF branch
| OP_VER -- ^ Transaction is invalid unless occuring in an unexecuted OP_IF branch
| OP_VERIF -- ^ Transaction is invalid even when occuring in an unexecuted OP_IF branch
| OP_VERNOTIF -- ^ Transaction is invalid even when occuring in an unexecuted OP_IF branch
| OP_RESERVED1 -- ^ Transaction is invalid unless occuring in an unexecuted OP_IF branch
| OP_RESERVED2 -- ^ Transaction is invalid unless occuring in an unexecuted OP_IF branch
-- pseudo
| OP_INVALIDOPCODE -- ^ this is a pseudo opcode, but it appears in the testnet3 blockchain...
| OP_UNKNOWN !Word8 -- ^ unknown opcodes also appear in the testnet, inside (unexecuted?) OP_IF branches...
deriving (Eq,Show)
--------------------------------------------------------------------------------
-- * helper functions
-- | Note: this function returns 'Nothing' for @OP_0@ (which technically pushes an empty array to the stack).
-- Also we don't check for validity (see 'is_valid_pushdata')
is_op_pushdata :: Opcode -> Maybe B.ByteString
is_op_pushdata op = case op of { OP_PUSHDATA _ bs -> Just bs ; _ -> Nothing }
-- | Note that OP_0 = push empty array, so we accept that as a valid @OP_PUSHDATA@
is_valid_pushdata :: Word8 -> B.ByteString -> Bool
is_valid_pushdata op bs
| op>=0 && op<=75 = (n == fromIntegral op)
| op==76 = (n <= 255)
| op==77 = (n <= 65535)
| op==78 = True
| otherwise = False
where
n = B.length bs
is_op_smallnum :: Opcode -> Maybe Int
is_op_smallnum op = case op of { OP_SMALLNUM k -> Just k ; _ -> Nothing }
is_nop :: Word8 -> Bool
is_nop w8 = case w8 of
0x61 -> True -- OP_NOP
_ | w8 >= 0xb0 && w8 <= 0xb9 -> True -- OP_NOP1 .. OP_NOP10
_ -> False
--------------------------------------------------------------------------------
showOpcode :: Opcode -> (String -> String)
showOpcode op = case op of
OP_PUSHDATA w8 dat -> showString "OP_PUSHDATA <0x"
. showString (showHexWord8 w8)
. showString "> \""
. showString (toHexStringChars dat)
. showChar '\"'
_ -> shows op
showList' :: (a -> (String -> String)) -> [a] -> (String -> String)
showList' _ [] s = "[]" ++ s
showList' showx (x:xs) s = '[' : showx x (showl xs)
where
showl [] = ']' : s
showl (y:ys) = ',' : showx y (showl ys)
showOpcodeList :: [Opcode] -> (String -> String)
showOpcodeList list = showList' showOpcode list
--------------------------------------------------------------------------------