summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2023-11-30 12:38:44 -0800
committerJohn Wiegley <johnw@newartisans.com>2024-08-05 08:35:56 -0700
commit75e5eacebaf8b9ad3a18b6f1a91a8cb227fa5c9c (patch)
tree896d7f2d1496bd19b2c5ba7a8b97285908bfde0c
parentf230125ecc0761e6bd082ac0d916db4f17f0278d (diff)
downloadfork-ledger-75e5eacebaf8b9ad3a18b6f1a91a8cb227fa5c9c.tar.gz
fork-ledger-75e5eacebaf8b9ad3a18b6f1a91a8cb227fa5c9c.tar.bz2
fork-ledger-75e5eacebaf8b9ad3a18b6f1a91a8cb227fa5c9c.zip
Add support for --hashes=sha512_256 as another algorithm
-rw-r--r--doc/ledger.111
-rw-r--r--doc/ledger3.texi9
-rw-r--r--src/generate.cc2
-rw-r--r--src/global.cc2
-rw-r--r--src/journal.cc5
-rw-r--r--src/journal.h6
-rw-r--r--src/precmd.cc2
-rw-r--r--src/session.cc6
-rw-r--r--src/session.h7
-rw-r--r--src/textual.cc18
-rw-r--r--src/utils.h6
-rw-r--r--src/xact.cc5
-rw-r--r--src/xact.h2
-rw-r--r--test/baseline/opt-hashes-sha512_256.test28
14 files changed, 81 insertions, 28 deletions
diff --git a/doc/ledger.1 b/doc/ledger.1
index febfaf1e..0e5ac7e7 100644
--- a/doc/ledger.1
+++ b/doc/ledger.1
@@ -1,4 +1,4 @@
-.Dd November 23, 2023
+.Dd November 30, 2023
.Dt LEDGER 1
.Os
.Sh NAME
@@ -639,7 +639,14 @@ Record the hash of each transaction in a
.Ar Hash
metadata value, according to the hashing algorithm given by the
.Ar ALGO
-argument. Note that if a
+argument. Support algorithms are
+.Ar sha512
+or
+.Ar sh512_256
+where both use SHA512, but the latter only stores the first half of
+the hash value.
+.Pp
+If a
.Ar Hash
metadata value is explicitly provided and does not match what would
have been generated, an error is reported. Hashes depend on previous
diff --git a/doc/ledger3.texi b/doc/ledger3.texi
index 494847ca..13219758 100644
--- a/doc/ledger3.texi
+++ b/doc/ledger3.texi
@@ -6765,6 +6765,15 @@ they do not match, an error is reported. You may also write just a
prefix of the @var{Hash}, which is less verbose but still gives quite
good assurance.
+The support algorithms are:
+
+@table @code
+@item sha512
+Use the SHA512 hashing algorithm.
+@item sha512_256
+Same as SHA512, but record only the first 256 bits.
+@end table
+
Somewhat like balance assertions, which give assurance that previous
posting amounts are correct, these @var{Hash} tags give assurance that
all previous journal entries (in parse order) are unchanged (or at
diff --git a/src/generate.cc b/src/generate.cc
index 98986c67..ee820c98 100644
--- a/src/generate.cc
+++ b/src/generate.cc
@@ -365,7 +365,7 @@ void generate_posts_iterator::increment()
parsing_context.get_current().journal = session.journal.get();
parsing_context.get_current().scope = &session;
- if (session.journal->read(parsing_context, false) != 0) {
+ if (session.journal->read(parsing_context, NO_HASHES) != 0) {
VERIFY(session.journal->xacts.back()->valid());
posts.reset(*session.journal->xacts.back());
post = *posts++;
diff --git a/src/global.cc b/src/global.cc
index ad1001ef..050a1d51 100644
--- a/src/global.cc
+++ b/src/global.cc
@@ -116,7 +116,7 @@ void global_scope_t::parse_init(path init_file)
parsing_context.get_current().journal = session().journal.get();
parsing_context.get_current().scope = &report();
- if (session().journal->read(parsing_context, false) > 0 ||
+ if (session().journal->read(parsing_context, NO_HASHES) > 0 ||
session().journal->auto_xacts.size() > 0 ||
session().journal->period_xacts.size() > 0) {
throw_(parse_error, _f("Transactions found in initialization file '%1%'")
diff --git a/src/journal.cc b/src/journal.cc
index b5ae3f1b..62e33440 100644
--- a/src/journal.cc
+++ b/src/journal.cc
@@ -466,7 +466,8 @@ bool journal_t::remove_xact(xact_t * xact)
return true;
}
-std::size_t journal_t::read(parse_context_stack_t& context, bool store_hashes)
+std::size_t journal_t::read(parse_context_stack_t& context,
+ hash_type_t hash_type)
{
std::size_t count = 0;
try {
@@ -485,7 +486,7 @@ std::size_t journal_t::read(parse_context_stack_t& context, bool store_hashes)
if (! current.master)
current.master = master;
- count = read_textual(context, store_hashes);
+ count = read_textual(context, hash_type);
if (count > 0) {
if (! current.pathname.empty())
sources.push_back(fileinfo_t(current.pathname));
diff --git a/src/journal.h b/src/journal.h
index d68618bf..cfc93c0a 100644
--- a/src/journal.h
+++ b/src/journal.h
@@ -184,7 +184,8 @@ public:
return period_xacts.end();
}
- std::size_t read(parse_context_stack_t& context, bool store_hashes);
+ std::size_t read(parse_context_stack_t& context,
+ hash_type_t hash_type);
bool has_xdata();
void clear_xdata();
@@ -193,7 +194,8 @@ public:
private:
- std::size_t read_textual(parse_context_stack_t& context, bool store_hashes);
+ std::size_t read_textual(parse_context_stack_t& context,
+ hash_type_t hash_type);
bool should_check_payees();
bool payee_not_registered(const string& name);
diff --git a/src/precmd.cc b/src/precmd.cc
index 25c28fc8..f90e8ced 100644
--- a/src/precmd.cc
+++ b/src/precmd.cc
@@ -74,7 +74,7 @@ namespace {
parsing_context.get_current().journal = report.session.journal.get();
parsing_context.get_current().scope = &report.session;
- report.session.journal->read(parsing_context, false);
+ report.session.journal->read(parsing_context, NO_HASHES);
report.session.journal->clear_xdata();
}
}
diff --git a/src/session.cc b/src/session.cc
index 4587f24a..f02ee9e9 100644
--- a/src/session.cc
+++ b/src/session.cc
@@ -134,7 +134,7 @@ std::size_t session_t::read_data(const string& master_account)
parsing_context.push(*price_db_path);
parsing_context.get_current().journal = journal.get();
try {
- if (journal->read(parsing_context, HANDLED(hashes_)) > 0)
+ if (journal->read(parsing_context, HANDLER(hashes_).hash_type) > 0)
throw_(parse_error, _("Transactions not allowed in price history file"));
}
catch (...) {
@@ -169,7 +169,7 @@ std::size_t session_t::read_data(const string& master_account)
parsing_context.get_current().journal = journal.get();
parsing_context.get_current().master = acct;
try {
- xact_count += journal->read(parsing_context, HANDLED(hashes_));
+ xact_count += journal->read(parsing_context, HANDLER(hashes_).hash_type);
}
catch (...) {
parsing_context.pop();
@@ -230,7 +230,7 @@ journal_t * session_t::read_journal_from_string(const string& data)
parsing_context.get_current().journal = journal.get();
parsing_context.get_current().master = journal->master;
try {
- journal->read(parsing_context, HANDLED(hashes_));
+ journal->read(parsing_context, HANDLER(hashes_).hash_type);
}
catch (...) {
parsing_context.pop();
diff --git a/src/session.h b/src/session.h
index ce1b69a3..c83be644 100644
--- a/src/session.h
+++ b/src/session.h
@@ -163,11 +163,6 @@ public:
data_files.push_back(str);
});
- enum hash_type_t {
- NO_HASHES = 0,
- HASH_SHA512 = 1
- };
-
OPTION__
(session_t, hashes_,
hash_type_t hash_type = NO_HASHES;
@@ -175,6 +170,8 @@ public:
DO_(str) {
if (str == "sha512" || str == "SHA512") {
hash_type = HASH_SHA512;
+ } else if (str == "sha512_256" || str == "SHA512_256") {
+ hash_type = HASH_SHA512_256;
} else {
throw_(std::invalid_argument, _f("Unrecognized hash type"));
}
diff --git a/src/textual.cc b/src/textual.cc
index af208232..5488c6a3 100644
--- a/src/textual.cc
+++ b/src/textual.cc
@@ -40,6 +40,7 @@
#include "query.h"
#include "pstream.h"
#include "pool.h"
+#include "session.h"
#include <algorithm>
#if HAVE_BOOST_PYTHON
#include "pyinterp.h"
@@ -79,7 +80,7 @@ namespace {
instance_t * parent;
std::list<application_t> apply_stack;
bool no_assertions;
- bool store_hashes;
+ hash_type_t hash_type;
#if TIMELOG_SUPPORT
time_log_t timelog;
#endif
@@ -88,10 +89,10 @@ namespace {
parse_context_t& _context,
instance_t * _parent = NULL,
const bool _no_assertions = false,
- const bool _store_hashes = false)
+ const hash_type_t _hash_type = NO_HASHES)
: context_stack(_context_stack), context(_context),
in(*context.stream.get()), parent(_parent),
- no_assertions(_no_assertions), store_hashes(_store_hashes),
+ no_assertions(_no_assertions), hash_type(_hash_type),
timelog(context) {}
virtual string description() {
@@ -805,7 +806,7 @@ void instance_t::include_directive(char * line)
context_stack.get_current().scope = scope;
try {
instance_t instance(context_stack, context_stack.get_current(),
- this, no_assertions, store_hashes);
+ this, no_assertions, hash_type);
instance.apply_stack.push_front(application_t("account", master));
instance.parse();
}
@@ -2020,11 +2021,12 @@ xact_t * instance_t::parse_xact(char * line,
TRACE_STOP(xact_details, 1);
- if (store_hashes) {
+ if (hash_type != NO_HASHES) {
string expected_hash =
xact->hash(previous_xact &&
previous_xact->has_tag("Hash") ?
- previous_xact->get_tag("Hash")->to_string() : "");
+ previous_xact->get_tag("Hash")->to_string() : "",
+ hash_type);
if (xact->has_tag("Hash")) {
string current_hash = xact->get_tag("Hash")->to_string();
if (! std::equal(expected_hash.begin(),
@@ -2059,13 +2061,13 @@ expr_t::ptr_op_t instance_t::lookup(const symbol_t::kind_t kind,
}
std::size_t journal_t::read_textual(parse_context_stack_t& context_stack,
- bool store_hashes)
+ hash_type_t hash_type)
{
TRACE_START(parsing_total, 1, "Total time spent parsing text:");
{
instance_t instance(context_stack, context_stack.get_current(), NULL,
checking_style == journal_t::CHECK_PERMISSIVE,
- store_hashes);
+ hash_type);
instance.apply_stack.push_front
(application_t("account", context_stack.get_current().master));
instance.parse();
diff --git a/src/utils.h b/src/utils.h
index c17c8fb1..34308807 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -605,6 +605,12 @@ inline string sha1sum(const string& str)
extern const string version;
+enum hash_type_t {
+ NO_HASHES = 0,
+ HASH_SHA512 = 1,
+ HASH_SHA512_256 = 2
+};
+
} // namespace ledger
/*@}*/
diff --git a/src/xact.cc b/src/xact.cc
index 5c3c7042..ab53794a 100644
--- a/src/xact.cc
+++ b/src/xact.cc
@@ -594,7 +594,7 @@ namespace {
}
}
-string xact_t::hash(string nonce) const {
+string xact_t::hash(string nonce, hash_type_t hash_type) const {
std::ostringstream repr;
repr << nonce;
repr << date();
@@ -621,7 +621,8 @@ string xact_t::hash(string nonce) const {
unsigned char data[128];
string repr_str(repr.str());
SHA512((void *)repr_str.c_str(), repr_str.length(), data);
- return bufferToHex(data, 64 /*SHA512_DIGEST_LENGTH*/);
+ return bufferToHex(
+ data, hash_type == HASH_SHA512 ? 64 : 32 /*SHA512_DIGEST_LENGTH*/);
}
namespace {
diff --git a/src/xact.h b/src/xact.h
index 2118c390..379ce778 100644
--- a/src/xact.h
+++ b/src/xact.h
@@ -128,7 +128,7 @@ public:
virtual bool valid() const;
- string hash(string nonce) const;
+ string hash(string nonce, hash_type_t hash_type) const;
};
class auto_xact_t : public xact_base_t
diff --git a/test/baseline/opt-hashes-sha512_256.test b/test/baseline/opt-hashes-sha512_256.test
new file mode 100644
index 00000000..f269652d
--- /dev/null
+++ b/test/baseline/opt-hashes-sha512_256.test
@@ -0,0 +1,28 @@
+2008/01/01 January
+ Expenses:Books $10.00
+ Assets:Cash
+
+2008/01/31 End of January
+ Expenses:Books $10.00
+ Assets:Cash
+
+2008/02/01 February
+ Assets:Cash
+ Expenses:Books $20.00
+
+test print --hashes=sha512_256
+2008/01/01 January
+ ; Hash: c0f3322857def0d32bd2c9398e0785aacc8206f9ba64ef923978cbdcea13f59c
+ Expenses:Books $10.00
+ Assets:Cash
+
+2008/01/31 End of January
+ ; Hash: 151aa236cc3c632e492a94739d92fa76f321939dac04102b4c5ef893384ac839
+ Expenses:Books $10.00
+ Assets:Cash
+
+2008/02/01 February
+ ; Hash: 0336590b40e1fc83d6ade4a8c8e8f1c75af10deaac2be80580fbb73b0086324d
+ Assets:Cash
+ Expenses:Books $20.00
+end test