summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2023-11-24 23:33:59 -0800
committerJohn Wiegley <johnw@newartisans.com>2024-08-05 08:35:56 -0700
commit67de3bce35de8158cd25750901a322cc02071c95 (patch)
tree984346a9cdd73dcd7c9dbc9175143e1f759a8263
parent6e9fda06df1754f58c347e44a4579d059dfd36f0 (diff)
downloadfork-ledger-67de3bce35de8158cd25750901a322cc02071c95.tar.gz
fork-ledger-67de3bce35de8158cd25750901a322cc02071c95.tar.bz2
fork-ledger-67de3bce35de8158cd25750901a322cc02071c95.zip
Make xact hashes independent of posting order
Also, support matching provided hashes against a prefixed of the generated hash.
-rw-r--r--doc/ledger3.texi12
-rw-r--r--src/item.cc6
-rw-r--r--src/item.h4
-rw-r--r--src/post.cc24
-rw-r--r--src/post.h2
-rw-r--r--src/textual.cc6
-rw-r--r--src/xact.cc34
-rw-r--r--src/xact.h2
8 files changed, 50 insertions, 40 deletions
diff --git a/doc/ledger3.texi b/doc/ledger3.texi
index b4f3baab..ad41b985 100644
--- a/doc/ledger3.texi
+++ b/doc/ledger3.texi
@@ -6760,9 +6760,15 @@ Record the hash of each transaction in a @var{Hash} metadata value,
according to the hashing algorithm given by the @var{ALGO}
argument. Note that if a @var{Hash} metadata value is explicitly
provided and does not match what would have been generated, an error is
-reported. Hashes depend on previous entries, such that setting a single
-hash value is sufficient to guarantee the shape of the entire history
-leading up to that entry.
+reported. This provided value can also just be a prefix, at whatever
+length the user wishes, in which case only this amount of text is
+compared to the generated hash. Printing the entry with hashes enabled
+will display both this explicit metadata setting and the internally
+generated hash value.
+
+Hashes depend on previous entries, such that setting a single hash value
+is sufficient to guarantee the shape of the entire history leading up to
+that entry.
@item --head @var{INT}
@itemx --first @var{INT}
diff --git a/src/item.cc b/src/item.cc
index df0ce920..c5db3cad 100644
--- a/src/item.cc
+++ b/src/item.cc
@@ -135,13 +135,17 @@ item_t::set_tag(const string& tag,
string_map::iterator i = metadata->find(tag);
if (i == metadata->end()) {
+ DEBUG("item.meta", "Setting new metadata value");
std::pair<string_map::iterator, bool> result
= metadata->insert(string_map::value_type(tag, tag_data_t(data, false)));
assert(result.second);
return result.first;
} else {
- if (overwrite_existing)
+ DEBUG("item.meta", "Found old metadata value");
+ if (overwrite_existing) {
+ DEBUG("item.meta", "Overwriting old metadata value");
(*i).second = tag_data_t(data, false);
+ }
return i;
}
}
diff --git a/src/item.h b/src/item.h
index 9221098e..84f062ab 100644
--- a/src/item.h
+++ b/src/item.h
@@ -199,10 +199,6 @@ public:
return _state;
}
- virtual string hash(string _nonce) const {
- return "";
- }
-
virtual void define(const symbol_t::kind_t, const string&,
expr_t::ptr_op_t);
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
diff --git a/src/post.cc b/src/post.cc
index 9d799a9e..e2063a36 100644
--- a/src/post.cc
+++ b/src/post.cc
@@ -671,30 +671,6 @@ void post_t::set_reported_account(account_t * acct)
acct->xdata().reported_posts.push_back(this);
}
-extern "C" unsigned char *SHA512(
- void *data, unsigned int data_len, unsigned char *digest);
-
-namespace {
- std::string bufferToHex(const unsigned char* buffer, std::size_t size) {
- std::ostringstream oss;
- oss << std::hex << std::setfill('0');
- for(std::size_t i = 0; i < size; ++i)
- oss << std::setw(2) << static_cast<int>(buffer[i]);
- return oss.str();
- }
-}
-
-string post_t::hash(string nonce) const {
- unsigned char data[128];
- std::ostringstream repr;
- repr << nonce;
- repr << account->fullname();
- repr << amount.to_string();
- string repr_str(repr.str());
- SHA512((void *)repr_str.c_str(), repr_str.length(), data);
- return bufferToHex(data, 64 /*SHA512_DIGEST_LENGTH*/);
-}
-
void extend_post(post_t& post, journal_t& journal)
{
commodity_t& comm(post.amount.commodity());
diff --git a/src/post.h b/src/post.h
index 48093463..48b614bf 100644
--- a/src/post.h
+++ b/src/post.h
@@ -245,8 +245,6 @@ public:
return const_cast<post_t *>(this)->reported_account();
}
- virtual string hash(string nonce) const;
-
friend class xact_t;
struct compare_by_date_and_sequence
diff --git a/src/textual.cc b/src/textual.cc
index 9ad62602..af208232 100644
--- a/src/textual.cc
+++ b/src/textual.cc
@@ -2027,10 +2027,12 @@ xact_t * instance_t::parse_xact(char * line,
previous_xact->get_tag("Hash")->to_string() : "");
if (xact->has_tag("Hash")) {
string current_hash = xact->get_tag("Hash")->to_string();
- if (expected_hash != current_hash) {
+ if (! std::equal(expected_hash.begin(),
+ expected_hash.begin() +
+ std::min(expected_hash.size(), current_hash.size()),
+ current_hash.begin()))
throw_(parse_error, _f("Expected hash %1% != %2%") %
expected_hash % current_hash);
- }
} else {
xact->set_tag("Hash", string_value(expected_hash));
}
diff --git a/src/xact.cc b/src/xact.cc
index 0b9bd9cd..5c3c7042 100644
--- a/src/xact.cc
+++ b/src/xact.cc
@@ -581,6 +581,19 @@ bool xact_t::valid() const
return true;
}
+extern "C" unsigned char *SHA512(
+ void *data, unsigned int data_len, unsigned char *digest);
+
+namespace {
+ std::string bufferToHex(const unsigned char* buffer, std::size_t size) {
+ std::ostringstream oss;
+ oss << std::hex << std::setfill('0');
+ for(std::size_t i = 0; i < size; ++i)
+ oss << std::setw(2) << static_cast<int>(buffer[i]);
+ return oss.str();
+ }
+}
+
string xact_t::hash(string nonce) const {
std::ostringstream repr;
repr << nonce;
@@ -588,12 +601,27 @@ string xact_t::hash(string nonce) const {
repr << aux_date();
repr << code;
repr << payee;
- string hash(repr.str());
posts_list all_posts(posts.begin(), posts.end());
+ std::vector<std::string> strings;
foreach (post_t * post, all_posts) {
- hash = post->hash(hash);
+ std::ostringstream posting;
+ posting << post->account->fullname();
+ if (! post->amount.is_null())
+ posting << post->amount.to_fullstring();
+ if (post->cost)
+ posting << post->cost->to_fullstring();
+ posting << post->checkin;
+ posting << post->checkout;
+ strings.push_back(posting.str());
+ }
+ std::sort(strings.begin(), strings.end());
+ foreach (string& str, strings) {
+ repr << str;
}
- return hash;
+ 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*/);
}
namespace {
diff --git a/src/xact.h b/src/xact.h
index 4c6fcd73..2118c390 100644
--- a/src/xact.h
+++ b/src/xact.h
@@ -128,7 +128,7 @@ public:
virtual bool valid() const;
- virtual string hash(string nonce) const;
+ string hash(string nonce) const;
};
class auto_xact_t : public xact_base_t