module Ripple.Sign (signTransaction) where

import Data.Word (Word32)
import Control.Applicative ((<$>))
import Control.Arrow (first)
import Data.Binary (encode)
import qualified Data.Serialize as Serialize
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LZ

import Crypto.Hash.CryptoAPI (SHA512, Hash(..), hash)
import Crypto.Types.PubKey.ECDSA (PrivateKey(..))
import Crypto.Random (CryptoRandomGen, GenError)
import ECDSA (sign, publicToBytes, publicFromPrivate, signatureEncodeDER)

import Ripple.Transaction

signTransaction :: (CryptoRandomGen g) =>
	Transaction -> PrivateKey -> g -> Either GenError (Transaction, g)
signTransaction (Transaction fs) private g =
	first (addSig . signatureEncodeDER) <$> sign private hash g
	where
	addSig s = Transaction ((TransactionSignature $ LZ.fromChunks [s]) : fs')
	hash = signing_hash (Transaction fs')
	fs' = SigningPublicKey publicKey : filter (not.isSPK) fs
	publicKey = LZ.fromChunks [publicToBytes $ publicFromPrivate private]

isSPK :: Field -> Bool
isSPK (SigningPublicKey {}) = True
isSPK _ = False

hash_sign :: Word32
hash_sign = 0x53545800

compute_hash :: (Hash ctx d) => Transaction -> d
compute_hash t = hash (encode hash_sign `LZ.append` encode t)

signing_hash :: Transaction -> BS.ByteString
signing_hash t = BS.take 32 $ Serialize.encode sha512
	where
	sha512 = compute_hash t :: SHA512