diff options
author | John Wiegley <johnw@newartisans.com> | 2023-11-24 23:33:59 -0800 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2024-08-05 08:35:56 -0700 |
commit | 67de3bce35de8158cd25750901a322cc02071c95 (patch) | |
tree | 984346a9cdd73dcd7c9dbc9175143e1f759a8263 | |
parent | 6e9fda06df1754f58c347e44a4579d059dfd36f0 (diff) | |
download | fork-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.texi | 12 | ||||
-rw-r--r-- | src/item.cc | 6 | ||||
-rw-r--r-- | src/item.h | 4 | ||||
-rw-r--r-- | src/post.cc | 24 | ||||
-rw-r--r-- | src/post.h | 2 | ||||
-rw-r--r-- | src/textual.cc | 6 | ||||
-rw-r--r-- | src/xact.cc | 34 | ||||
-rw-r--r-- | src/xact.h | 2 |
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; } } @@ -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()); @@ -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 { @@ -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 |