-
Notifications
You must be signed in to change notification settings - Fork 211
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CIP-1855: Add primitives for MintBurn policy key derivation
- Add primitives for deriving policy keys used to mint/burn assets. Implemented according to [CIP-1855](https://github.com/cardano-foundation/CIPs/blob/b2e9d02cb9a71ba9e754a432c78197428abf7e4c/CIP-1855/CIP-1855.md). - Add "PolicyK" option to the "Depth" enum. - Add simple tests for key derivation.
- Loading branch information
1 parent
b722abc
commit c05ea21
Showing
4 changed files
with
204 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
99 changes: 99 additions & 0 deletions
99
lib/core/src/Cardano/Wallet/Primitive/AddressDerivation/MintBurn.hs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
{-# LANGUAGE DataKinds #-} | ||
|
||
-- | | ||
-- Copyright: © 2018-2021 IOHK | ||
-- License: Apache-2.0 | ||
-- | ||
-- Derivation of policy keys which are used to create scripts for the purposes | ||
-- of minting and burning. Derived according to CIP-1855 | ||
-- (https://github.com/cardano-foundation/CIPs/blob/b2e9d02cb9a71ba9e754a432c78197428abf7e4c/CIP-1855/CIP-1855.md). | ||
-- | ||
-- The policy keys are derived from the following path: | ||
-- | ||
-- m / purpose' / coin_type' / policy_ix' | ||
-- m / 1855' / 1815' / [2^31 .. 2^32-1]' | ||
-- | ||
-- Where purpose' and coin_type' are fixed, and each new policy_ix' represents a | ||
-- different policy key. | ||
|
||
module Cardano.Wallet.Primitive.AddressDerivation.MintBurn | ||
( -- * Constants | ||
purposeCIP1855 | ||
-- * Helpers | ||
, derivePolicyKey | ||
, derivePolicyPrivateKey | ||
) where | ||
|
||
import Prelude | ||
|
||
import Cardano.Address.Derivation | ||
( XPrv ) | ||
import Cardano.Address.Script | ||
( KeyHash ) | ||
import Cardano.Crypto.Wallet | ||
( deriveXPrv ) | ||
import Cardano.Crypto.Wallet.Types | ||
( DerivationScheme (DerivationScheme2) ) | ||
import Cardano.Wallet.Primitive.AddressDerivation | ||
( Depth (PolicyK, PurposeK, RootK) | ||
, DerivationType (Hardened) | ||
, Index (getIndex) | ||
, Index (Index) | ||
, Passphrase (Passphrase) | ||
, WalletKey (publicKey) | ||
, getRawKey | ||
, hashVerificationKey | ||
, liftRawKey | ||
) | ||
import Cardano.Wallet.Primitive.AddressDiscovery | ||
( coinTypeAda ) | ||
|
||
import qualified Cardano.Address.Script as CA | ||
|
||
-- | Purpose for forged policy keys is a constant set to 1855' (or 0x8000073F) | ||
-- following the original CIP-1855: "Forging policy keys for HD Wallets". | ||
-- | ||
-- It indicates that the subtree of this node is used according to this | ||
-- specification. | ||
-- | ||
-- Hardened derivation is used at this level. | ||
purposeCIP1855 :: Index 'Hardened 'PurposeK | ||
purposeCIP1855 = toEnum 0x8000073F | ||
|
||
-- | Derive the policy private key that should be used to create mint/burn | ||
-- scripts. | ||
derivePolicyPrivateKey | ||
:: Passphrase purpose | ||
-- ^ Passphrase for wallet | ||
-> XPrv | ||
-- ^ Root private key to derive policy private key from | ||
-> Index 'Hardened 'PolicyK | ||
-- ^ Index of policy script | ||
-> XPrv | ||
-- ^ Policy private key | ||
derivePolicyPrivateKey (Passphrase pwd) rootXPrv (Index policyIx) = | ||
let | ||
purposeXPrv = -- lvl1 derivation; hardened derivation of purpose' | ||
deriveXPrv DerivationScheme2 pwd rootXPrv (getIndex purposeCIP1855) | ||
coinTypeXPrv = -- lvl2 derivation; hardened derivation of coin_type' | ||
deriveXPrv DerivationScheme2 pwd purposeXPrv (getIndex coinTypeAda) | ||
-- lvl3 derivation; hardened derivation of policy' index | ||
in deriveXPrv DerivationScheme2 pwd coinTypeXPrv policyIx | ||
|
||
-- | Derive the policy private key that should be used to create mint/burn | ||
-- scripts, as well as the key hash of the policy public key. | ||
derivePolicyKey | ||
:: WalletKey key | ||
=> Passphrase "encryption" | ||
-- ^ Passphrase for wallet | ||
-> key 'RootK XPrv | ||
-- ^ Root private key to derive policy private key from | ||
-> Index 'Hardened 'PolicyK | ||
-- ^ Index of policy script | ||
-> (key 'PolicyK XPrv, KeyHash) | ||
-- ^ Policy private key | ||
derivePolicyKey pwd rootPrv policyIx = (policyK, vkeyHash) | ||
where | ||
policyK = liftRawKey policyPrv | ||
policyPrv = derivePolicyPrivateKey pwd (getRawKey rootPrv) policyIx | ||
vkeyHash = hashVerificationKey CA.Payment (publicKey policyK) |
95 changes: 95 additions & 0 deletions
95
lib/core/test/unit/Cardano/Wallet/Primitive/AddressDerivation/MintBurnSpec.hs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
{-# LANGUAGE DataKinds #-} | ||
{-# LANGUAGE FlexibleInstances #-} | ||
|
||
{-# OPTIONS_GHC -Wno-orphans #-} | ||
|
||
module Cardano.Wallet.Primitive.AddressDerivation.MintBurnSpec | ||
( spec | ||
) where | ||
|
||
import Prelude | ||
|
||
import Cardano.Address.Derivation | ||
( XPrv, XPub ) | ||
import Cardano.Address.Script | ||
( KeyHash ) | ||
import Cardano.Wallet.Primitive.AddressDerivation | ||
( Depth (PolicyK, RootK, ScriptK) | ||
, DerivationType (Hardened) | ||
, Index | ||
, Passphrase | ||
, WalletKey (publicKey) | ||
, getRawKey | ||
, hashVerificationKey | ||
, liftRawKey | ||
) | ||
import Cardano.Wallet.Primitive.AddressDerivation.MintBurn | ||
( derivePolicyKey, derivePolicyPrivateKey ) | ||
import Cardano.Wallet.Primitive.AddressDerivation.Shelley | ||
( ShelleyKey ) | ||
import Cardano.Wallet.Primitive.AddressDerivationSpec | ||
() | ||
import Cardano.Wallet.Unsafe | ||
( unsafeXPrv ) | ||
import qualified Data.ByteString as BS | ||
import Test.Hspec | ||
( Spec, describe, it ) | ||
import Test.Hspec.Extra | ||
( parallel ) | ||
import Test.QuickCheck | ||
( Arbitrary (..), Property, property, vector, (===) ) | ||
import Test.QuickCheck.Arbitrary | ||
( arbitraryBoundedEnum ) | ||
|
||
import qualified Cardano.Address.Script as CA | ||
|
||
spec :: Spec | ||
spec = do | ||
parallel $ describe "Mint/Burn Policy key Address Derivation Properties" $ do | ||
it "Policy key derivation from master key works for various indexes" $ | ||
property prop_keyDerivationFromXPrv | ||
it "Policy public key hash matches private key" $ | ||
property prop_keyHashMatchesXPrv | ||
|
||
{------------------------------------------------------------------------------- | ||
Properties | ||
-------------------------------------------------------------------------------} | ||
|
||
prop_keyDerivationFromXPrv | ||
:: Passphrase "encryption" | ||
-> XPrv | ||
-> Index 'Hardened 'PolicyK | ||
-> Property | ||
prop_keyDerivationFromXPrv pwd masterkey policyIx = | ||
rndKey `seq` property () -- NOTE Making sure this doesn't throw | ||
where | ||
rndKey :: XPrv | ||
rndKey = derivePolicyPrivateKey pwd masterkey policyIx | ||
|
||
prop_keyHashMatchesXPrv | ||
:: Passphrase "encryption" | ||
-> ShelleyKey 'RootK XPrv | ||
-> Index 'Hardened 'PolicyK | ||
-> Property | ||
prop_keyHashMatchesXPrv pwd masterkey policyIx = | ||
hashVerificationKey | ||
CA.Payment | ||
(getPublicKey rndKey) | ||
=== keyHash | ||
where | ||
rndKey :: ShelleyKey 'PolicyK XPrv | ||
keyHash :: KeyHash | ||
(rndKey, keyHash) = derivePolicyKey pwd masterkey policyIx | ||
|
||
getPublicKey | ||
:: ShelleyKey 'PolicyK XPrv | ||
-> ShelleyKey 'ScriptK XPub | ||
getPublicKey = | ||
publicKey . (liftRawKey :: XPrv -> ShelleyKey 'ScriptK XPrv) . getRawKey | ||
|
||
instance Arbitrary XPrv where | ||
arbitrary = unsafeXPrv . BS.pack <$> vector 128 | ||
|
||
instance Arbitrary (Index 'Hardened 'PolicyK) where | ||
shrink _ = [] | ||
arbitrary = arbitraryBoundedEnum |