module Network.EasyBitcoin.Internal.Script
where
import Data.Word
import qualified Data.ByteString as BS
import Data.List (nub)
import Data.Binary (Binary, get, put)
import Data.Binary.Get ( getWord64be
, getWord32be
, getWord8
, getWord16le
, getWord32le
, getByteString
, Get
, isEmpty
)
import Data.Binary.Put( putWord64be
, putWord32be
, putWord32le
, putWord16le
, putWord8
, putByteString
)
import Control.Monad (unless, guard,replicateM,forM_,liftM2)
import Control.Applicative((<$>))
newtype Script = Script{scriptOps::[ScriptOp]}
deriving (Eq, Show, Ord,Read)
data ScriptOp = OP_PUSHDATA { pushContent :: !BS.ByteString
, pushOpCode :: !PushDataType
}
| OP_1NEGATE
| OP_RESERVED
| OP__ Word8
| OP_NOP
| OP_VER
| OP_IF
| OP_NOTIF
| OP_VERIF
| OP_VERNOTIF
| OP_ELSE
| OP_ENDIF
| OP_VERIFY
| OP_RETURN
| 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
| OP_CAT
| OP_SUBSTR
| OP_LEFT
| OP_RIGHT
| OP_SIZE
| OP_INVERT
| OP_AND
| OP_OR
| OP_XOR
| OP_EQUAL
| OP_EQUALVERIFY
| OP_RESERVED1
| OP_RESERVED2
| 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
| OP_RIPEMD160
| OP_SHA1
| OP_SHA256
| OP_HASH160
| OP_HASH256
| OP_CODESEPARATOR
| OP_CHECKSIG
| OP_CHECKSIGVERIFY
| OP_CHECKMULTISIG
| OP_CHECKMULTISIGVERIFY
| OP_NOP1 | OP_NOP2 | OP_NOP3 | OP_NOP4 | OP_NOP5
| OP_NOP6 | OP_NOP7 | OP_NOP8 | OP_NOP9 | OP_NOP10
| OP_PUBKEYHASH
| OP_PUBKEY
| OP_INVALIDOPCODE !Word8
deriving (Show, Read, Ord,Eq)
data PushDataType = OPCODE
| OPDATA1
| OPDATA2
| OPDATA4
deriving (Show, Ord,Read, Eq)
op:: Int -> ScriptOp
op n = let n_ = min 16 (max 0 n)
in OP__ (fromIntegral n)
opPushData :: BS.ByteString -> ScriptOp
opPushData bs = case BS.length bs of
len | 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"
opNumber :: ScriptOp -> Maybe Int
opNumber (OP__ n) = Just (fromIntegral n)
opNumber _ = Nothing
opContent :: ScriptOp -> Maybe BS.ByteString
opContent (OP_PUSHDATA content _) = Just content
opContent _ = Nothing
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
instance Binary ScriptOp where
get = do op <- getWord8
case op of
0x00 -> return $ OP__ 0
_ | op <= 0x4b -> do payload <- getByteString (fromIntegral op)
return $ OP_PUSHDATA payload OPCODE
0x4c -> do len <- getWord8
payload <- getByteString (fromIntegral len)
return $ OP_PUSHDATA payload OPDATA1
0x4d -> do len <- getWord16le
payload <- getByteString (fromIntegral len)
return $ OP_PUSHDATA payload OPDATA2
0x4e -> do len <- getWord32le
payload <- getByteString (fromIntegral len)
return $ OP_PUSHDATA payload OPDATA4
0x4f -> return $ OP_1NEGATE
0x50 -> return $ OP_RESERVED
_ | op < 0x61 -> return $ OP__ (op 0x50)
0x61 -> return $ OP_NOP
0x62 -> return $ OP_VER
0x63 -> return $ OP_IF
0x64 -> return $ OP_NOTIF
0x65 -> return $ OP_VERIF
0x66 -> return $ OP_VERNOTIF
0x67 -> return $ OP_ELSE
0x68 -> return $ OP_ENDIF
0x69 -> return $ OP_VERIFY
0x6a -> return $ OP_RETURN
0x6b -> return $ OP_TOALTSTACK
0x6c -> return $ OP_FROMALTSTACK
0x6d -> return $ OP_2DROP
0x6e -> return $ OP_2DUP
0x6f -> return $ OP_3DUP
0x70 -> return $ OP_2OVER
0x71 -> return $ OP_2ROT
0x72 -> return $ OP_2SWAP
0x73 -> return $ OP_IFDUP
0x74 -> return $ OP_DEPTH
0x75 -> return $ OP_DROP
0x76 -> return $ OP_DUP
0x77 -> return $ OP_NIP
0x78 -> return $ OP_OVER
0x79 -> return $ OP_PICK
0x7a -> return $ OP_ROLL
0x7b -> return $ OP_ROT
0x7c -> return $ OP_SWAP
0x7d -> return $ OP_TUCK
0x7e -> return $ OP_CAT
0x7f -> return $ OP_SUBSTR
0x80 -> return $ OP_LEFT
0x81 -> return $ OP_RIGHT
0x82 -> return $ OP_SIZE
0x83 -> return $ OP_INVERT
0x84 -> return $ OP_AND
0x85 -> return $ OP_OR
0x86 -> return $ OP_XOR
0x87 -> return $ OP_EQUAL
0x88 -> return $ OP_EQUALVERIFY
0x89 -> return $ OP_RESERVED1
0x8a -> return $ OP_RESERVED2
0x8b -> return $ OP_1ADD
0x8c -> return $ OP_1SUB
0x8d -> return $ OP_2MUL
0x8e -> return $ OP_2DIV
0x8f -> return $ OP_NEGATE
0x90 -> return $ OP_ABS
0x91 -> return $ OP_NOT
0x92 -> return $ OP_0NOTEQUAL
0x93 -> return $ OP_ADD
0x94 -> return $ OP_SUB
0x95 -> return $ OP_MUL
0x96 -> return $ OP_DIV
0x97 -> return $ OP_MOD
0x98 -> return $ OP_LSHIFT
0x99 -> return $ OP_RSHIFT
0x9a -> return $ OP_BOOLAND
0x9b -> return $ OP_BOOLOR
0x9c -> return $ OP_NUMEQUAL
0x9d -> return $ OP_NUMEQUALVERIFY
0x9e -> return $ OP_NUMNOTEQUAL
0x9f -> return $ OP_LESSTHAN
0xa0 -> return $ OP_GREATERTHAN
0xa1 -> return $ OP_LESSTHANOREQUAL
0xa2 -> return $ OP_GREATERTHANOREQUAL
0xa3 -> return $ OP_MIN
0xa4 -> return $ OP_MAX
0xa5 -> return $ OP_WITHIN
0xa6 -> return $ OP_RIPEMD160
0xa7 -> return $ OP_SHA1
0xa8 -> return $ OP_SHA256
0xa9 -> return $ OP_HASH160
0xaa -> return $ OP_HASH256
0xab -> return $ OP_CODESEPARATOR
0xac -> return $ OP_CHECKSIG
0xad -> return $ OP_CHECKSIGVERIFY
0xae -> return $ OP_CHECKMULTISIG
0xaf -> return $ OP_CHECKMULTISIGVERIFY
0xb0 -> return $ OP_NOP1
0xb1 -> return $ OP_NOP2
0xb2 -> return $ OP_NOP3
0xb3 -> return $ OP_NOP4
0xb4 -> return $ OP_NOP5
0xb5 -> return $ OP_NOP6
0xb6 -> return $ OP_NOP7
0xb7 -> return $ OP_NOP8
0xb8 -> return $ OP_NOP9
0xb9 -> return $ OP_NOP10
0xfd -> return $ OP_PUBKEYHASH
0xfe -> return $ OP_PUBKEY
_ -> 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 <= 0x7fffffff) $ fail "OP_PUSHDATA OPDATA4: Payload size too big"
putWord8 0x4e
putWord32le $ fromIntegral len
putByteString payload
OP__ 0 -> putWord8 0x00
OP_1NEGATE -> putWord8 0x4f
OP_RESERVED -> putWord8 0x50
OP__ n -> putWord8 (0x50+n)
OP_PUBKEY -> putWord8 0xfe
OP_PUBKEYHASH -> putWord8 0xfd
(OP_INVALIDOPCODE x) -> putWord8 x
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
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
OP_CAT -> putWord8 0x7e
OP_SUBSTR -> putWord8 0x7f
OP_LEFT -> putWord8 0x80
OP_RIGHT -> putWord8 0x81
OP_SIZE -> putWord8 0x82
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
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
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
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