Skip to content

Commit

Permalink
Merge pull request #529 from Concordium/add-multi-sig-signing-support…
Browse files Browse the repository at this point in the history
…-in-concordium-client

Add SignedTransaction type
  • Loading branch information
DOBEN authored May 24, 2024
2 parents 5b18d8c + 377398d commit 1c97f30
Show file tree
Hide file tree
Showing 9 changed files with 430 additions and 5 deletions.
1 change: 1 addition & 0 deletions concordium-base.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ test-suite test
Types.AmountSpec
Types.ParametersSpec
Types.PayloadSerializationSpec
Types.PayloadSpec
Types.TransactionSerializationSpec
Types.TransactionSummarySpec
Types.UpdatesSpec
Expand Down
17 changes: 17 additions & 0 deletions haskell-src/Concordium/Crypto/EncryptedTransfers.hs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,15 @@ instance FromJSON EncryptedAmountTransferData where
eatdProof <- v .: "proof"
return EncryptedAmountTransferData{..}

instance ToJSON EncryptedAmountTransferData where
toJSON EncryptedAmountTransferData{..} =
object
[ "remainingAmount" .= eatdRemainingAmount,
"transferAmount" .= eatdTransferAmount,
"index" .= eatdIndex,
"proof" .= eatdProof
]

withEncryptedAmountTransferData ::
EncryptedAmountTransferData ->
(Ptr ElgamalCipher -> Ptr ElgamalCipher -> Ptr ElgamalCipher -> Ptr ElgamalCipher -> EncryptedAmountAggIndex -> Word64 -> Ptr CChar -> IO a) ->
Expand Down Expand Up @@ -491,6 +500,14 @@ instance FromJSON SecToPubAmountTransferData where
stpatdProof <- v .: "proof"
return SecToPubAmountTransferData{..}

instance ToJSON SecToPubAmountTransferData where
toJSON SecToPubAmountTransferData{..} =
object
[ "remainingAmount" .= stpatdRemainingAmount,
"transferAmount" .= stpatdTransferAmount,
"index" .= stpatdIndex,
"proof" .= stpatdProof
]
withSecToPubAmountTransferData ::
SecToPubAmountTransferData ->
(Ptr ElgamalCipher -> Ptr ElgamalCipher -> Word64 -> EncryptedAmountAggIndex -> Word64 -> Ptr CChar -> IO a) ->
Expand Down
8 changes: 8 additions & 0 deletions haskell-src/Concordium/ID/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,7 @@ instance Serialize CredentialDeploymentInformation where
instance Eq CredentialDeploymentInformation where
cdi1 == cdi2 = cdiValues cdi1 == cdiValues cdi2

-- Implement `FromJSON` instance for `CredentialDeploymentInformation`.
instance FromJSON CredentialDeploymentInformation where
parseJSON = withObject "CredentialDeploymentInformation" $ \x -> do
cdiValues <- parseJSON (Object x)
Expand All @@ -797,6 +798,13 @@ instance FromJSON CredentialDeploymentInformation where
}
Left _ -> fail "\"proofs\" is not a valid base16 string."

-- Implement `ToJSON` instance for `CredentialDeploymentInformation`.
instance ToJSON CredentialDeploymentInformation where
toJSON CredentialDeploymentInformation{..} =
object $
("proofs" .= cdiProofs)
: credentialDeploymentValuesList cdiValues

-- | Information about the account that should be created as part of the initial
-- credential deployment.
data InitialCredentialAccount = InitialCredentialAccount
Expand Down
2 changes: 1 addition & 1 deletion haskell-src/Concordium/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,7 @@ instance Show Address where

-- | Time in seconds since the unix epoch
newtype TransactionTime = TransactionTime {ttsSeconds :: Word64}
deriving (Show, Read, Eq, Num, Ord, FromJSON, ToJSON, Real, Enum, Integral) via Word64
deriving (Show, Read, Eq, Num, Ord, Real, FromJSON, ToJSON, Enum, Integral) via Word64

instance S.Serialize TransactionTime where
put = P.putWord64be . ttsSeconds
Expand Down
255 changes: 255 additions & 0 deletions haskell-src/Concordium/Types/Execution.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}

module Concordium.Types.Execution where
Expand All @@ -18,6 +19,7 @@ import Concordium.Utils.Serialization
import Data.Aeson ((.:), (.=))
import qualified Data.Aeson as AE
import Data.Aeson.TH
import Data.Aeson.Types (Parser)
import Data.Bits
import qualified Data.ByteString as BS
import qualified Data.ByteString.Short as BSS
Expand Down Expand Up @@ -159,6 +161,29 @@ bakerKeysWithProofsSize :: Int
bakerKeysWithProofsSize =
VRF.publicKeySize + dlogProofSize + Sig.publicKeySize + dlogProofSize + Bls.publicKeySize + Bls.proofSize

-- Implement `ToJSON` instance for `BakerKeysWithProofs`.
instance AE.ToJSON BakerKeysWithProofs where
toJSON BakerKeysWithProofs{..} =
AE.object
[ "electionVerifyKey" AE..= bkwpElectionVerifyKey,
"electionKeyOwnershipProof" AE..= bkwpProofElection,
"signatureVerifyKey" AE..= bkwpSignatureVerifyKey,
"signatureKeyOwnershipProof" AE..= bkwpProofSig,
"aggregationVerifyKey" AE..= bkwpAggregationVerifyKey,
"aggregationKeyOwnershipProof" AE..= bkwpProofAggregation
]

-- Implement `FromJSON` instance for `BakerKeysWithProofs`.
instance AE.FromJSON BakerKeysWithProofs where
parseJSON = AE.withObject "BakerKeysWithProofs" $ \obj -> do
bkwpElectionVerifyKey <- obj AE..: "electionVerifyKey"
bkwpProofElection <- obj AE..: "electionKeyOwnershipProof"
bkwpSignatureVerifyKey <- obj AE..: "signatureVerifyKey"
bkwpProofSig <- obj AE..: "signatureKeyOwnershipProof"
bkwpAggregationVerifyKey <- obj AE..: "aggregationVerifyKey"
bkwpProofAggregation <- obj AE..: "aggregationKeyOwnershipProof"
return BakerKeysWithProofs{..}

-- | The transaction payload. Defines the supported kinds of transactions.
--
-- * @SPEC: <$DOCS/Transactions#transaction-body>
Expand Down Expand Up @@ -405,6 +430,235 @@ instance S.Serialize TransactionType where
20 -> return TTConfigureDelegation
n -> fail $ "Unrecognized TransactionType tag: " ++ show n

instance AE.ToJSON Payload where
-- `mod` was renamed to `module`
toJSON DeployModule{..} = AE.object ["module" AE..= dmMod, "transactionType" AE..= AE.String "deployModule"]
toJSON InitContract{..} =
AE.object
[ "amount" AE..= icAmount,
"modRef" AE..= icModRef,
"initName" AE..= icInitName,
"param" AE..= icParam,
"transactionType" AE..= AE.String "initContract"
]
toJSON Update{..} =
AE.object
[ "amount" AE..= uAmount,
"address" AE..= uAddress,
"receiveName" AE..= uReceiveName,
"message" AE..= uMessage,
"transactionType" AE..= AE.String "update"
]
toJSON Transfer{..} =
AE.object
["toAddress" AE..= tToAddress, "amount" AE..= tAmount, "transactionType" AE..= AE.String "transfer"]
toJSON UpdateCredentialKeys{..} =
AE.object
[ "credId" AE..= uckCredId,
"keys" AE..= uckKeys,
"transactionType" AE..= AE.String "updateCredentialKeys"
]
toJSON EncryptedAmountTransfer{..} =
AE.object
[ "to" AE..= eatTo,
"data" AE..= eatData,
"transactionType" AE..= AE.String "encryptedAmountTransfer"
]
toJSON TransferToEncrypted{..} = AE.object ["amount" AE..= tteAmount, "transactionType" AE..= AE.String "transferToEncrypted"]
toJSON TransferToPublic{..} = AE.object ["data" AE..= ttpData, "transactionType" AE..= AE.String "transferToPublic"]
toJSON TransferWithSchedule{..} =
AE.object
[ "to" AE..= twsTo,
"schedule" AE..= twsSchedule,
"transactionType" AE..= AE.String "transferWithSchedule"
]
toJSON UpdateCredentials{..} =
AE.object
[ "newCredInfos" AE..= ucNewCredInfos,
"removeCredIds" AE..= ucRemoveCredIds,
"newThreshold" AE..= ucNewThreshold,
"transactionType" AE..= AE.String "updateCredentials"
]
toJSON RegisterData{..} = AE.object ["data" AE..= rdData, "transactionType" AE..= AE.String "registerData"]
toJSON TransferWithMemo{..} =
AE.object
[ "toAddress" AE..= twmToAddress,
"memo" AE..= twmMemo,
"amount" AE..= twmAmount,
"transactionType" AE..= AE.String "transferWithMemo"
]
toJSON EncryptedAmountTransferWithMemo{..} =
AE.object
[ "to" AE..= eatwmTo,
"memo" AE..= eatwmMemo,
"data" AE..= eatwmData,
"transactionType" AE..= AE.String "encryptedAmountTransferWithMemo"
]
toJSON TransferWithScheduleAndMemo{..} =
AE.object
[ "to" AE..= twswmTo,
"memo" AE..= twswmMemo,
"schedule" AE..= twswmSchedule,
"transactionType" AE..= AE.String "transferWithScheduleAndMemo"
]
-- `configureBaker` was renamed to `configureValidator`
toJSON ConfigureBaker{..} =
AE.object
[ "capital" AE..= cbCapital,
"restakeEarnings" AE..= cbRestakeEarnings,
"openForDelegation" AE..= cbOpenForDelegation,
"keysWithProofs" AE..= cbKeysWithProofs,
"metadataURL" AE..= cbMetadataURL,
"transactionFeeCommission" AE..= cbTransactionFeeCommission,
"bakingRewardCommission" AE..= cbBakingRewardCommission,
"finalizationRewardCommission" AE..= cbFinalizationRewardCommission,
"transactionType" AE..= AE.String "configureValidator"
]
toJSON ConfigureDelegation{..} =
AE.object
[ "capital" AE..= cdCapital,
"restakeEarnings" AE..= cdRestakeEarnings,
"delegationTarget" AE..= cdDelegationTarget,
"transactionType" AE..= AE.String "configureDelegation"
]
toJSON AddBaker{..} =
AE.object
[ "electionVerifyKey" AE..= abElectionVerifyKey,
"signatureVerifyKey" AE..= abSignatureVerifyKey,
"aggregationVerifyKey" AE..= abAggregationVerifyKey,
"proofSig" AE..= abProofSig,
"proofElection" AE..= abProofElection,
"proofAggregation" AE..= abProofAggregation,
"bakingStake" AE..= abBakingStake,
"restakeEarnings" AE..= abRestakeEarnings,
"transactionType" AE..= AE.String "addBaker"
]
toJSON RemoveBaker = AE.object ["transactionType" AE..= AE.String "removeBaker"]
toJSON UpdateBakerStake{..} = AE.object ["stake" AE..= ubsStake, "transactionType" AE..= AE.String "updateBakerStake"]
toJSON UpdateBakerRestakeEarnings{..} =
AE.object
[ "restakeEarnings" AE..= ubreRestakeEarnings,
"transactionType" AE..= AE.String "updateBakerRestakeEarnings"
]
toJSON UpdateBakerKeys{..} =
AE.object
[ "electionVerifyKey" AE..= ubkElectionVerifyKey,
"signatureVerifyKey" AE..= ubkSignatureVerifyKey,
"aggregationVerifyKey" AE..= ubkAggregationVerifyKey,
"proofSig" AE..= ubkProofSig,
"proofElection" AE..= ubkProofElection,
"proofAggregation" AE..= ubkProofAggregation,
"transactionType" AE..= AE.String "updateBakerKeys"
]

instance AE.FromJSON Payload where
parseJSON = AE.withObject "payload" $ \obj -> do
transactionType <- obj AE..: "transactionType" :: Parser String

case transactionType of
"deployModule" -> do
dmMod <- obj AE..: "module"
return DeployModule{..}
"initContract" -> do
icAmount <- obj AE..: "amount"
icModRef <- obj AE..: "modRef"
icInitName <- obj AE..: "initName"
icParam <- obj AE..: "param"
return InitContract{..}
"update" -> do
uAmount <- obj AE..: "amount"
uAddress <- obj AE..: "address"
uReceiveName <- obj AE..: "receiveName"
uMessage <- obj AE..: "message"
return Update{..}
"transfer" -> do
tToAddress <- obj AE..: "toAddress"
tAmount <- obj AE..: "amount"
return Transfer{..}
"UpdateBakerStake" -> do
ubsStake <- obj AE..: "stake"
return UpdateBakerStake{..}
"updateBakerRestakeEarnings" -> do
ubreRestakeEarnings <- obj AE..: "restakeEarnings"
return UpdateBakerRestakeEarnings{..}
"updateBakerKeys" -> do
ubkElectionVerifyKey <- obj AE..: "electionVerifyKey"
ubkSignatureVerifyKey <- obj AE..: "signatureVerifyKey"
ubkAggregationVerifyKey <- obj AE..: "aggregationVerifyKey"
ubkProofSig <- obj AE..: "proofSig"
ubkProofElection <- obj AE..: "proofElection"
ubkProofAggregation <- obj AE..: "proofAggregation"
return UpdateBakerKeys{..}
"updateCredentialKeys" -> do
uckCredId <- obj AE..: "credId"
uckKeys <- obj AE..: "keys"
return UpdateCredentialKeys{..}
"removeBaker" -> do
return RemoveBaker
"addBaker" -> do
abElectionVerifyKey <- obj AE..: "electionVerifyKey"
abSignatureVerifyKey <- obj AE..: "signatureVerifyKey"
abAggregationVerifyKey <- obj AE..: "aggregationVerifyKey"
abProofSig <- obj AE..: "proofSig"
abProofElection <- obj AE..: "proofElection"
abProofAggregation <- obj AE..: "proofAggregation"
abBakingStake <- obj AE..: "bakingStake"
abRestakeEarnings <- obj AE..: "restakeEarnings"
return AddBaker{..}
"encryptedAmountTransfer" -> do
eatTo <- obj AE..: "to"
eatData <- obj AE..: "data"
return EncryptedAmountTransfer{..}
"transferToEncrypted" -> do
tteAmount <- obj AE..: "amount"
return TransferToEncrypted{..}
"transferToPublic" -> do
ttpData <- obj AE..: "data"
return TransferToPublic{..}
"transferWithSchedule" -> do
twsTo <- obj AE..: "to"
twsSchedule <- obj AE..: "schedule"
return TransferWithSchedule{..}
"updateCredentials" -> do
ucNewCredInfos <- obj AE..: "newCredInfos"
ucRemoveCredIds <- obj AE..: "removeCredIds"
ucNewThreshold <- obj AE..: "newThreshold"
return UpdateCredentials{..}
"registerData" -> do
rdData <- obj AE..: "data"
return RegisterData{..}
"transferWithMemo" -> do
twmToAddress <- obj AE..: "toAddress"
twmMemo <- obj AE..: "memo"
twmAmount <- obj AE..: "amount"
return TransferWithMemo{..}
"encryptedAmountTransferWithMemo" -> do
eatwmTo <- obj AE..: "to"
eatwmMemo <- obj AE..: "memo"
eatwmData <- obj AE..: "data"
return EncryptedAmountTransferWithMemo{..}
"transferWithScheduleAndMemo" -> do
twswmTo <- obj AE..: "to"
twswmMemo <- obj AE..: "memo"
twswmSchedule <- obj AE..: "schedule"
return TransferWithScheduleAndMemo{..}
"configureValidator" -> do
cbCapital <- obj AE..: "capital"
cbRestakeEarnings <- obj AE..: "restakeEarnings"
cbOpenForDelegation <- obj AE..: "openForDelegation"
cbKeysWithProofs <- obj AE..: "keysWithProofs"
cbMetadataURL <- obj AE..: "metadataURL"
cbTransactionFeeCommission <- obj AE..: "transactionFeeCommission"
cbBakingRewardCommission <- obj AE..: "bakingRewardCommission"
cbFinalizationRewardCommission <- obj AE..: "finalizationRewardCommission"
return ConfigureBaker{..}
"configureDelegation" -> do
cdCapital <- obj AE..: "capital"
cdRestakeEarnings <- obj AE..: "restakeEarnings"
cdDelegationTarget <- obj AE..: "delegationTarget"
return ConfigureDelegation{..}
_ -> fail "Unrecognized 'TransactionType' tag"

-- | Payload serialization according to
--
-- * @SPEC: <$DOCS/Transactions#transaction-body>
Expand Down Expand Up @@ -2266,6 +2520,7 @@ instance S.Serialize RejectReason where
n -> fail $ "Unrecognized RejectReason tag: " ++ show n

instance AE.ToJSON RejectReason

instance AE.FromJSON RejectReason

-- | Reasons for the execution of a transaction to fail on the current block state.
Expand Down
6 changes: 2 additions & 4 deletions haskell-src/Concordium/Types/Transactions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -526,15 +526,13 @@ signTransactionSingle kp = signTransaction [(0, [(0, kp)])]
-- * @SPEC: <$DOCS/Transactions#transaction-signature>
signTransaction :: [(CredentialIndex, [(KeyIndex, KeyPair)])] -> TransactionHeader -> EncodedPayload -> AccountTransaction
signTransaction keys atrHeader atrPayload =
let
atrSignHash = transactionSignHashFromHeaderPayload atrHeader atrPayload
let atrSignHash = transactionSignHashFromHeaderPayload atrHeader atrPayload
-- only sign the hash of the transaction
bodyHash = transactionSignHashToByteString atrSignHash
credSignature cKeys = Map.fromList $ map (\(idx, key) -> (idx, SigScheme.sign key bodyHash)) cKeys
tsSignatures = Map.fromList $ map (\(idx, cKeys) -> (idx, credSignature cKeys)) keys
atrSignature = TransactionSignature{..}
in
AccountTransaction{..}
in AccountTransaction{..}

-- | Verify credential signatures. This checks
--
Expand Down
Loading

0 comments on commit 1c97f30

Please sign in to comment.