Skip to main content

grin_core/core/
committed.rs

1// Copyright 2021 The Grin Developers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! The Committed trait and associated errors.
16
17use keychain::BlindingFactor;
18use util::secp::key::SecretKey;
19use util::secp::pedersen::Commitment;
20use util::{secp, secp_static, static_secp_instance};
21
22/// Errors from summing and verifying kernel excesses via committed trait.
23#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)]
24pub enum Error {
25	/// Keychain related error.
26	#[error("Keychain error {0}")]
27	Keychain(keychain::Error),
28	/// Secp related error.
29	#[error("Secp error {0}")]
30	Secp(secp::Error),
31	/// Kernel sums do not equal output sums.
32	#[error("Kernel sum mismatch")]
33	KernelSumMismatch,
34	/// Committed overage (fee or reward) is invalid
35	#[error("Invalid value")]
36	InvalidValue,
37}
38
39impl From<secp::Error> for Error {
40	fn from(e: secp::Error) -> Error {
41		Error::Secp(e)
42	}
43}
44
45impl From<keychain::Error> for Error {
46	fn from(e: keychain::Error) -> Error {
47		Error::Keychain(e)
48	}
49}
50
51/// Implemented by types that hold inputs and outputs (and kernels)
52/// containing Pedersen commitments.
53/// Handles the collection of the commitments as well as their
54/// summing, taking potential explicit overages of fees into account.
55pub trait Committed {
56	/// Gather the kernel excesses and sum them.
57	fn sum_kernel_excesses(
58		&self,
59		offset: &BlindingFactor,
60	) -> Result<(Commitment, Commitment), Error> {
61		// then gather the kernel excess commitments
62		let kernel_commits = self.kernels_committed();
63
64		// sum the commitments
65		let kernel_sum = sum_commits(kernel_commits, vec![])?;
66
67		// sum the commitments along with the
68		// commit to zero built from the offset
69		let kernel_sum_plus_offset = {
70			let secp = static_secp_instance();
71			let secp = secp.lock();
72			let mut commits = vec![kernel_sum];
73			if *offset != BlindingFactor::zero() {
74				let key = offset.secret_key(&secp)?;
75				let offset_commit = secp.commit(0, key)?;
76				commits.push(offset_commit);
77			}
78			secp.commit_sum(commits, vec![])?
79		};
80
81		Ok((kernel_sum, kernel_sum_plus_offset))
82	}
83
84	/// Gathers commitments and sum them.
85	fn sum_commitments(&self, overage: i64) -> Result<Commitment, Error> {
86		// gather the commitments
87		let mut input_commits = self.inputs_committed();
88		let mut output_commits = self.outputs_committed();
89
90		// add the overage as output commitment if positive,
91		// or as an input commitment if negative
92		if overage != 0 {
93			let over_commit = {
94				let secp = static_secp_instance();
95				let secp = secp.lock();
96				let overage_abs = overage.checked_abs().ok_or_else(|| Error::InvalidValue)? as u64;
97				secp.commit_value(overage_abs).unwrap()
98			};
99			if overage < 0 {
100				input_commits.push(over_commit);
101			} else {
102				output_commits.push(over_commit);
103			}
104		}
105
106		sum_commits(output_commits, input_commits)
107	}
108
109	/// Vector of input commitments to verify.
110	fn inputs_committed(&self) -> Vec<Commitment>;
111
112	/// Vector of output commitments to verify.
113	fn outputs_committed(&self) -> Vec<Commitment>;
114
115	/// Vector of kernel excesses to verify.
116	fn kernels_committed(&self) -> Vec<Commitment>;
117
118	/// Verify the sum of the kernel excesses equals the
119	/// sum of the outputs, taking into account both
120	/// the kernel_offset and overage.
121	fn verify_kernel_sums(
122		&self,
123		overage: i64,
124		kernel_offset: BlindingFactor,
125	) -> Result<(Commitment, Commitment), Error> {
126		// Sum all input|output|overage commitments.
127		let utxo_sum = self.sum_commitments(overage)?;
128
129		// Sum the kernel excesses accounting for the kernel offset.
130		let (kernel_sum, kernel_sum_plus_offset) = self.sum_kernel_excesses(&kernel_offset)?;
131
132		if utxo_sum != kernel_sum_plus_offset {
133			return Err(Error::KernelSumMismatch);
134		}
135
136		Ok((utxo_sum, kernel_sum))
137	}
138}
139
140/// Utility to sum positive and negative commitments, eliminating zero values
141pub fn sum_commits(
142	mut positive: Vec<Commitment>,
143	mut negative: Vec<Commitment>,
144) -> Result<Commitment, Error> {
145	let zero_commit = secp_static::commit_to_zero_value();
146	positive.retain(|x| *x != zero_commit);
147	negative.retain(|x| *x != zero_commit);
148	let secp = static_secp_instance();
149	let secp = secp.lock();
150	Ok(secp.commit_sum(positive, negative)?)
151}
152
153/// Utility function to take sets of positive and negative kernel offsets as
154/// blinding factors, convert them to private key filtering zero values and
155/// summing all of them. Useful to build blocks.
156pub fn sum_kernel_offsets(
157	positive: Vec<BlindingFactor>,
158	negative: Vec<BlindingFactor>,
159) -> Result<BlindingFactor, Error> {
160	let secp = static_secp_instance();
161	let secp = secp.lock();
162	let positive = to_secrets(positive, &secp);
163	let negative = to_secrets(negative, &secp);
164
165	if positive.is_empty() {
166		Ok(BlindingFactor::zero())
167	} else {
168		let sum = secp.blind_sum(positive, negative)?;
169		Ok(BlindingFactor::from_secret_key(sum))
170	}
171}
172
173fn to_secrets(bf: Vec<BlindingFactor>, secp: &secp::Secp256k1) -> Vec<SecretKey> {
174	bf.into_iter()
175		.filter(|x| *x != BlindingFactor::zero())
176		.filter_map(|x| x.secret_key(&secp).ok())
177		.collect::<Vec<_>>()
178}