Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Obsoleting the sending of DBCs #288

Merged
merged 3 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ A user has a main key - `MainKey` - which is a `bls::SecretKey`.
It is in essense a key _pair_, as the corresponding `bls::PublicKey` can be gotten from that secret key.
The `bls::PublicKey`of that key pair, is the `PublicAddress` to which anyone can send tokens.

- A `Dbc` is a vehicle for transfering tokens.
- A `Dbc` is a container that holds value (counted in tokens).

- A `Dbc` has a unique identifier, `DbcId`, which is a `bls::PublicKey`.
The corresponding `bls::SecretKey` called `DerivedKey`, unlocks the value.

- A `Dbc` can only be fully spent. So you unlock it and take out all the tokens, and the `Dbc` is spent.

- A `Dbc` cannot be made public as it contains secrets, what the Network only ever sees is `SignedSpend`, which tells us which `Dbc` was spent

### Sending tokens:
When you send tokens to someone, you create a new `Dbc`, with a `DbcId` (a `bls::PublicKey`) by deriving it from the `PublicAddress` (a `bls::PublicKey`) of someone. You derive it using a random `DerivationIndex`, which you include (encrypted to the `PublicAddress`, which means only the corresponding `MainKey` can decrypt it) in the newly created `Dbc`.
When you send tokens to someone, you create a new `Dbc`, with a `DbcId` (a `bls::PublicKey`) by deriving it from the `PublicAddress` (a `bls::PublicKey`) of someone. You derive it using a random `DerivationIndex`, which you include in the newly created `Dbc`.
Also included in this new `Dbc` are the signatures of network nodes verifying that the input `Dbc(s)` that you emptied to create this new `Dbc`, are actually spent and was included in the transaction where this new `Dbc` was created. (The signatures part will change with the new network design.)
Since `Dbc`s contain secrets, they should be encrypted before being sent.

### Unknown connection between Dbcs and PublicAddresses:
Since the `DbcId` is derived from the `PublicAddress`, using a secret `DerivationIndex`, no one except sender and receiver knows that this new `Dbc` was sent to the `PublicAddress` of the receiver.
The recipient decrypts the `DerivationIndex` cipher, using their `MainKey` (remember, it's the corresponding `bls::SecretKey` of the `bls::PublicKey` in the `PublicAddress`).
4 changes: 2 additions & 2 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
transaction::{DbcTransaction, Output},
DbcId, DerivationIndex, DerivedKey, FeeOutput, Input, PublicAddress, Spend,
};
use crate::{Dbc, DbcCiphers, Error, Hash, Result, SignedSpend, Token};
use crate::{Dbc, DbcSecrets, Error, Hash, Result, SignedSpend, Token};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
Expand Down Expand Up @@ -237,7 +237,7 @@ impl DbcBuilder {
Dbc {
id: public_address.new_dbc_id(derivation_index),
src_tx: self.spent_tx.clone(),
ciphers: DbcCiphers::from((public_address, derivation_index)),
secrets: DbcSecrets::from((public_address, derivation_index)),
signed_spends: self.signed_spends.clone(),
},
output.token,
Expand Down
28 changes: 14 additions & 14 deletions src/dbc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// permissions and limitations relating to use of the SAFE Network Software.

use crate::{
dbc_id::PublicAddress, transaction::DbcTransaction, DbcCiphers, DbcId, DerivationIndex,
dbc_id::PublicAddress, transaction::DbcTransaction, DbcId, DbcSecrets, DerivationIndex,
DerivedKey, Error, FeeOutput, Hash, MainKey, Result, SignedSpend, Token,
};
#[cfg(feature = "serde")]
Expand Down Expand Up @@ -63,9 +63,9 @@ pub struct Dbc {
/// The transaction where this DBC was created.
#[debug(skip)]
pub src_tx: DbcTransaction,
/// Encrypted information for and about the recipient of this Dbc.
/// Secret information for and about the recipient of this Dbc.
#[debug(skip)]
pub ciphers: DbcCiphers,
pub secrets: DbcSecrets,
/// The transaction's input's SignedSpends
pub signed_spends: BTreeSet<SignedSpend>,
}
Expand All @@ -78,7 +78,7 @@ impl Dbc {

// Return PublicAddress from which DbcId is derived.
pub fn public_address(&self) -> &PublicAddress {
&self.ciphers.public_address
&self.secrets.public_address
}

/// Return DerivedKey using MainKey supplied by caller.
Expand All @@ -88,12 +88,12 @@ impl Dbc {
if &main_key.public_address() != self.public_address() {
return Err(Error::MainKeyDoesNotMatchPublicAddress);
}
Ok(main_key.derive_key(&self.derivation_index(main_key)?))
Ok(main_key.derive_key(&self.derivation_index()))
}

/// Return the derivation index that was used to derive DbcId and corresponding DerivedKey of a Dbc.
pub fn derivation_index(&self, main_key: &MainKey) -> Result<DerivationIndex> {
self.ciphers.derivation_index(main_key)
pub fn derivation_index(&self) -> DerivationIndex {
self.secrets.derivation_index
}

/// Return the fee output used in the source transaction
Expand Down Expand Up @@ -126,7 +126,7 @@ impl Dbc {
pub fn hash(&self) -> Hash {
let mut sha3 = Sha3::v256();
sha3.update(self.src_tx.hash().as_ref());
sha3.update(&self.ciphers.to_bytes());
sha3.update(&self.secrets.to_bytes());

for sp in self.signed_spends.iter() {
sha3.update(&sp.to_bytes());
Expand Down Expand Up @@ -216,11 +216,11 @@ pub(crate) mod tests {
outputs: vec![Output::new(derived_key.dbc_id(), amount)],
fee: FeeOutput::new(Hash::default(), 3_500, Hash::default()),
};
let ciphers = DbcCiphers::from((&main_key.public_address(), &derivation_index));
let secrets = DbcSecrets::from((&main_key.public_address(), &derivation_index));
let dbc = Dbc {
id: derived_key.dbc_id(),
src_tx: tx,
ciphers,
secrets,
signed_spends: Default::default(),
};

Expand All @@ -247,11 +247,11 @@ pub(crate) mod tests {
outputs: vec![Output::new(derived_key.dbc_id(), amount)],
fee: FeeOutput::new(Hash::default(), 2_500, Hash::default()),
};
let ciphers = DbcCiphers::from((&main_key.public_address(), &derivation_index));
let secrets = DbcSecrets::from((&main_key.public_address(), &derivation_index));
let dbc = Dbc {
id: derived_key.dbc_id(),
src_tx: tx,
ciphers,
secrets,
signed_spends: Default::default(),
};

Expand Down Expand Up @@ -303,11 +303,11 @@ pub(crate) mod tests {
fee: FeeOutput::default(),
};

let ciphers = DbcCiphers::from((&main_key.public_address(), &derivation_index));
let secrets = DbcSecrets::from((&main_key.public_address(), &derivation_index));
let dbc = Dbc {
id: derived_key.dbc_id(),
src_tx: tx,
ciphers,
secrets,
signed_spends: Default::default(),
};

Expand Down
45 changes: 16 additions & 29 deletions src/dbc_ciphers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@
// KIND, either express or implied. Please review the Licences for the specific language governing
// permissions and limitations relating to use of the SAFE Network Software.

use crate::{dbc_id::PublicAddress, DerivationIndex, Hash, MainKey, Result};
use blsttc::Ciphertext;
use crate::{dbc_id::PublicAddress, DerivationIndex, Hash};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use tiny_keccak::{Hasher, Sha3};

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct DbcCiphers {
pub struct DbcSecrets {
/// This is the PublicAddress to which tokens are send. The PublicAddress may be published
/// and multiple payments sent to this address by various parties. It is useful for
/// accepting donations, for example.
Expand All @@ -26,56 +25,44 @@ pub struct DbcCiphers {
/// and never seen by the spentbook nodes.
///
/// The DbcId used in the transaction is derived from this PublicAddress using a random
/// derivation index, which is stored (encrypted) in derivation_index_cipher.
/// derivation index, which is stored in derivation_index.
pub public_address: PublicAddress,

/// This indicates which index to use when deriving the DbcId of the
/// Dbc, from the PublicAddress.
///
/// This index is stored in encrypted form, and is encrypted to the PublicAddress.
/// So the actual PublicAddress the tokens in this Dbc was sent to, is unknown to
/// anyone not in posession of the MainKey corresponding to the above mentioned PublicAddress.
pub derivation_index_cipher: Ciphertext,
pub derivation_index: DerivationIndex,
}

/// Represents the ciphers of a Dbc.
impl From<(PublicAddress, Ciphertext)> for DbcCiphers {
// Create a new DbcCiphers for signing.
fn from(params: (PublicAddress, Ciphertext)) -> Self {
let (public_address, derivation_index_cipher) = params;
/// Represents the Secrets of a Dbc.
impl From<(PublicAddress, DerivationIndex)> for DbcSecrets {
// Create a new DbcSecrets for signing.
fn from(params: (PublicAddress, DerivationIndex)) -> Self {
let (public_address, derivation_index) = params;
Self {
public_address,
derivation_index_cipher,
derivation_index,
}
}
}

/// Represents the ciphers of a Dbc.
impl From<(&PublicAddress, &DerivationIndex)> for DbcCiphers {
// Create a new DbcCiphers for signing.
/// Represents the Secrets of a Dbc.
impl From<(&PublicAddress, &DerivationIndex)> for DbcSecrets {
// Create a new DbcSecrets for signing.
fn from(params: (&PublicAddress, &DerivationIndex)) -> Self {
let (public_address, derivation_index) = params;
let derivation_index_cipher = public_address.encrypt(derivation_index);

Self {
public_address: *public_address,
derivation_index_cipher,
derivation_index: *derivation_index,
}
}
}

impl DbcCiphers {
pub(crate) fn derivation_index(&self, key_source: &MainKey) -> Result<DerivationIndex> {
let bytes = key_source.decrypt_index(&self.derivation_index_cipher)?;
let mut idx = [0u8; 32];
idx.copy_from_slice(&bytes[0..32]);
Ok(idx)
}

impl DbcSecrets {
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes: Vec<u8> = Default::default();
bytes.extend(&self.public_address.to_bytes());
bytes.extend(&self.derivation_index_cipher.to_bytes());
bytes.extend(&self.derivation_index);
bytes
}

Expand Down
44 changes: 12 additions & 32 deletions src/dbc_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

use crate::{
rand::{distributions::Standard, Rng, RngCore},
Error, PublicKey, Result,
PublicKey,
};
use blsttc::{serde_impl::SerdeSecret, Ciphertext, SecretKey, PK_SIZE};
use blsttc::{serde_impl::SerdeSecret, SecretKey, PK_SIZE};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -70,14 +70,14 @@ impl DerivedKey {
/// they generate the id of the Dbc - the DbcId - that shall hold the tokens.
/// The DbcId is generated from this PublicAddress, and only the sender
/// will at this point know that the DbcId is related to this PublicAddress.
/// When creating the Dbc using that DbcId, the sender will also encrypt the
/// When creating the Dbc using that DbcId, the sender will also include the
/// DerivationIndex that was used to generate the DbcId, so that the recipient behind
/// the PublicAddress can also see that the DbcId is related to this PublicAddress.
/// The recipient can then use the received DerivationIndex to generate the DerivedKey
/// corresponding to that DbcId, and thus unlock the value of the Dbc by using that DerivedKey.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Clone)]
pub struct PublicAddress(PublicKey);
pub struct PublicAddress(pub PublicKey);

impl PublicAddress {
pub fn new(public_key: PublicKey) -> Self {
Expand All @@ -97,11 +97,6 @@ impl PublicAddress {
DbcId(self.0.derive_child(index))
}

/// To send tokens to this address, a derivation index is encrypted
pub fn encrypt(&self, derivation_index: &DerivationIndex) -> Ciphertext {
self.0.encrypt(derivation_index)
}

pub fn to_bytes(self) -> [u8; PK_SIZE] {
self.0.to_bytes()
}
Expand All @@ -122,6 +117,11 @@ impl MainKey {
Self(SerdeSecret(secret_key))
}

/// Get the secret key.
pub fn secret_key(&self) -> &SecretKey {
&self.0
}

/// This is the static public address which is shared with others, and
/// to which payments can be made by getting a new unique identifier for a Dbc to be created.
pub fn public_address(&self) -> PublicAddress {
Expand All @@ -133,37 +133,17 @@ impl MainKey {
self.0.sign(msg)
}

/// When someone wants to send tokens to the PublicAddress of this MainKey,
/// they generate the id of the Dbc - the DbcId - that shall hold the tokens.
/// The created Dbc contains the encrypted derivation index, that is decrypted using
/// this MainKey instance.
/// The index is then used to derive the key - the DerivedKey - corresponding to the DbcId of the
/// Dbc sent to you. With that DerivedKey you will have access to the tokens in the Dbc.
pub fn decrypt_index(&self, derivation_index_cipher: &Ciphertext) -> Result<DerivationIndex> {
let bytes = self
.0
.decrypt(derivation_index_cipher)
.ok_or(Error::DecryptionBySecretKeyFailed)?;

let mut index = [0u8; 32];
index.copy_from_slice(&bytes[0..32]);

Ok(index)
}

/// Derive the key - the DerivedKey - corresponding to a DbcId
/// which was also derived using the same DerivationIndex.
///
/// When someone wants to send tokens to the PublicAddress of this MainKey,
/// they generate the id of the Dbc - the DbcId - that shall hold the tokens.
/// The recipient of the tokens, is the person/entity that holds this MainKey.
///
/// The created Dbc contains the _encrypted_ form of the derivation index that was used to
/// generate that very DbcId. The sender encrypted it so that no-one but the recipient of the
/// tokens in the Dbc (and the sender itself of course) shall be able to see which index was used.
/// This encrypted index is then decrypted by the recipient, using this MainKey instance (see `fn decrypt_index` above).
/// The created Dbc contains the derivation index that was used to
/// generate that very DbcId.
///
/// When passing the resulting decrypted derivation index to this function (`fn derive_key`),
/// When passing the derivation index to this function (`fn derive_key`),
/// a DerivedKey is generated corresponding to the DbcId. This DerivedKey can unlock the Dbc of that
/// DbcId, thus giving access to the tokens it holds.
/// By that, the recipient has received the tokens from the sender.
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub mod mock;
pub use crate::{
builder::{DbcBuilder, TransactionBuilder},
dbc::Dbc,
dbc_ciphers::DbcCiphers,
dbc_ciphers::DbcSecrets,
dbc_id::{random_derivation_index, DbcId, DerivationIndex, DerivedKey, MainKey, PublicAddress},
error::{Error, Result},
fee_output::FeeOutput,
Expand Down
Loading