• 查看、校验、归档… 带你掌握 openGauss 账本数据库


    账本数据库融合了区块链思想,将用户操作记录至两种历史表中:用户历史表和全局区块表。当用户创建防篡改用户表时,系统将自动为该表添加一个 hash 列来保存每行数据的 hash 摘要信息,同时在 blockchain 模式下会创建一张用户历史表来记录对应用户表中每条数据的变更行为;而用户对防篡改用户表的一次修改行为将记录至全局区块表中。由于历史表具有只可追加不可修改的特点,因此历史表记录串联起来便形成了用户对防篡改用户表的修改历史。

    操作步骤

    1. 创建防篡改模式。

    openGauss=# CREATE SCHEMA ledgernsp WITH BLOCKCHAIN;

    首先在这个 SQL 中我们可以看到 WITH BLOCKCHAIN ,这里说明创建出来的 SCHEMA 与普通的 SCHEMA 不同,但就行不同在哪里我们后面会提到。

    • 从语法解析看,增加了对 BLOCKCHAIN 的处理,标记了是否为账本模式。
    1. CreateSchema ::= CREATE SCHEMA schema_name
    2. [ AUTHORIZATION user_name ] [WITH BLOCKCHAIN] [ schema_element [ ... ] ];
    • CreateSchemaStmt 结构中增加了 bool 类型字段 hasBlockChain
    1. typedef struct CreateSchemaStmt {
    2. NodeTag type;
    3. char *schemaname; /* the name of the schema to create */
    4. char *authid; /* the owner of the created schema */
    5. bool hasBlockChain; /* whether this schema has blockchain */
    6. List *schemaElts; /* schema components (list of parsenodes) */
    7. TempType temptype; /* if the schema is temp table's schema */
    8. List *uuids; /* the list of uuid(only create sequence or table with serial type need) */
    9. } CreateSchemaStmt;

    你不知道的限制

    账本数据库对于 ALTER SCHEMA 的几个限制

    1)dbe_perf 和 snapshot 两个模式不能 ALTER 为 blockchain 模式。

    1. if (withBlockchain && ((strncmp(nspName, "dbe_perf", STR_SCHEMA_NAME_LENGTH) == 0) ||
    2. (strncmp(nspName, "snapshot", STR_SNAPSHOT_LENGTH) == 0))) {
    3. ereport(ERROR, (errcode(ERRCODE_OPERATE_FAILED),
    4. errmsg("The schema '%s' doesn't allow to alter to blockchain schema", nspName)));
    5. }

    2)系统模式不能 ALTER 为 blockchain 模式。

    1. if (withBlockchain && !g_instance.attr.attr_common.allowSystemTableMods &&
    2. !u_sess->attr.attr_common.IsInplaceUpgrade && IsReservedName(nspName))
    3. ereport(ERROR,
    4. (errcode(ERRCODE_RESERVED_NAME),
    5. errmsg("The system schema \"%s\" doesn't allow to alter to blockchain schema", nspName)));

    3)包含了表的 SCHEMA 不能 ALTER 为 blockchain 模式。

    1. /*
    2. * If the any table exists in the schema, do not change to ledger schema.
    3. */
    4. StringInfo existTbl = TableExistInSchema(HeapTupleGetOid(tup), TABLE_TYPE_ANY);
    5. if (existTbl->len != 0) {
    6. if (withBlockchain) {
    7. ereport(ERROR,
    8. (errcode(ERRCODE_RESERVED_NAME),
    9. errmsg("It is not supported to change \"%s\" to blockchain schema which includes tables.",
    10. nspName)));
    11. } else {
    12. ereport(ERROR,
    13. (errcode(ERRCODE_RESERVED_NAME),
    14. errmsg("It is not supported to change \"%s\" to normal schema which includes tables.",
    15. nspName)));
    16. }
    17. }

    查看模式

    2. 在防篡改模式下创建防篡改用户表。

    openGauss=# CREATE TABLE ledgernsp.usertable(id int, name text);

    你不知道的限制

    • 创建账本表的同时会自动创建一个 “历史表” 和 “历史表的索引”。

    在建表时 CreateCommand 会调用 AlterCreateChainTables,如果是账本表再去调用 create_hist_relation 来创建历史表

    CreateCommand -> AlterCreateChainTables -> create_hist_relation

    1. /*
    2. * AlterCreateChainTables
    3. * If it is a ledger usertable, that should invoking this function.
    4. * then create a history table.
    5. */
    6. void AlterCreateChainTables(Oid relOid, Datum reloptions, CreateStmt *mainTblStmt)
    7. {
    8. Relation rel = NULL;
    9. rel = heap_open(relOid, AccessExclusiveLock);
    10. /* Ledger user table only support for the regular relation. */
    11. if (!rel->rd_isblockchain) {
    12. heap_close(rel, NoLock);
    13. return;
    14. }
    15. create_hist_relation(rel, reloptions, mainTblStmt);
    16. heap_close(rel, NoLock);
    17. }
    • 历史表命名规则,参见函数 get_hist_name
    1. bool get_hist_name(Oid relid, const char *rel_name, char *hist_name, Oid nsp_oid, const char *nsp_name)
    2. {
    3. errno_t rc;
    4. if (!OidIsValid(relid) || rel_name == NULL) {
    5. return false;
    6. }
    7. nsp_oid = OidIsValid(nsp_oid) ? nsp_oid : get_rel_namespace(relid);
    8. nsp_name = (nsp_name == NULL) ? get_namespace_name(nsp_oid) : nsp_name;
    9. int part_hist_name_len = strlen(rel_name) + strlen(nsp_name) + 1;
    10. if (part_hist_name_len + strlen("_hist") >= NAMEDATALEN) {
    11. rc = snprintf_s(hist_name, NAMEDATALEN, NAMEDATALEN - 1, "%d_%d_hist", nsp_oid, relid);
    12. securec_check_ss(rc, "", "");
    13. } else {
    14. rc = snprintf_s(hist_name, NAMEDATALEN, NAMEDATALEN - 1, "%s_%s_hist", nsp_name, rel_name);
    15. securec_check_ss(rc, "", "");
    16. }
    17. return true;
    18. }
    • 表名最大长度 #define NAMEDATALEN 64
    • 如果没有超过长度限制:schema_table_hist
    • 如果超过长度限制:schema (oid)_talbe (oid)_hist,因为 oid 是 unsigned int 类型最大值为 4294967295 为 10 位,所以这种命名规则的最大长度为 10+1+10+1+4+\0=27,因此永远不会超过最大长度 64。
    1. omm=# create schema aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa with blockchain;
    2. CREATE SCHEMA
    3. omm=# create table aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(id int);
    4. CREATE TABLE
    • 历史表索引命名规则,参见函数 get_hist_name
    1. /* now create index for this new history table */
    2. char hist_index_name[NAMEDATALEN];
    3. rc = snprintf_s(hist_index_name, NAMEDATALEN, NAMEDATALEN - 1, "gs_hist_%u_index", relid);
    • 命名规则:gs_hist_$(账本表 oid)_index。

    3、修改防篡改用户表数据

    对防篡改用户表执行 INSERT/UPDATE/DELETE。

    1. openGauss=# INSERT INTO ledgernsp.usertable VALUES(1, 'alex'), (2, 'bob'), (3, 'peter');
    2. INSERT 0 3
    3. openGauss=# SELECT *, hash FROM ledgernsp.usertable ORDER BY id;
    4. id | name | hash
    5. ----+-------+------------------
    6. 1 | alex | 1f2e543c580cb8c5
    7. 2 | bob | 8fcd74a8a6a4b484
    8. 3 | peter | f51b4b1b12d0354b
    9. (3 rows)
    10. openGauss=# UPDATE ledgernsp.usertable SET name = 'bob2' WHERE id = 2;
    11. UPDATE 1
    12. openGauss=# SELECT *, hash FROM ledgernsp.usertable ORDER BY id;
    13. id | name | hash
    14. ----+-------+------------------
    15. 1 | alex | 1f2e543c580cb8c5
    16. 2 | bob2 | 437761affbb7c605
    17. 3 | peter | f51b4b1b12d0354b
    18. (3 rows)
    19. openGauss=# DELETE FROM ledgernsp.usertable WHERE id = 3;
    20. DELETE 1
    21. openGauss=# SELECT *, hash FROM ledgernsp.usertable ORDER BY id;
    22. id | name | hash
    23. ----+------+------------------
    24. 1 | alex | 1f2e543c580cb8c5
    25. 2 | bob2 | 437761affbb7c605
    26. (2 rows)

    查看账本历史操作记录

    官方文档

    前提条件

    • 系统中需要有审计管理员或者具有审计管理员权限的角色。
    • 数据库正常运行,并且对防篡改数据库执行了一系列增、删、改等操作,保证在查询时段内有账本操作记录结果产生。

    基本操作

    1、查询全局区块表记录。

    1. omm=# SELECT * FROM gs_global_chain;
    2. blocknum | dbname | username | starttime | relid | relnsp | relname | relhash | globalhash |
    3. txcommand
    4. ----------+--------+----------+-------------------------------+-------+-----------+-----------+------------------+----------------------------------+----------------
    5. --------------------------------------------------------------
    6. 1 | omm | omm | 2022-09-17 13:59:37.84824+00 | 16404 | ledgernsp | usertable | a41714001181a294 | 83927d11ba1fd678e8f4b0723a9cd5f2 | INSERT INTO led
    7. gernsp.usertable VALUES(1, 'alex'), (2, 'bob'), (3, 'peter');
    8. 2 | omm | omm | 2022-09-17 13:59:51.723068+00 | 16404 | ledgernsp | usertable | b3a9ed0755131181 | b5ee73b6c20c817230182f6373c78e20 | UPDATE ledgerns
    9. p.usertable SET name = 'bob2' WHERE id = 2;
    10. 3 | omm | omm | 2022-09-17 13:59:58.159596+00 | 16404 | ledgernsp | usertable | 0ae4b4e4ed2fcab5 | 0cc9938cf7f1ed7f7f1a03c29954380a | DELETE FROM led
    11. gernsp.usertable WHERE id = 3;
    12. (3 rows)
    • 注册钩子,在对账本做修改操作的时候注册的钩子函数 ledger_ExecutorEnd 被回调。
    1. /*
    2. * ledger_hook_init -- install of gchain block record hook.
    3. */
    4. void ledger_hook_init(void)
    5. {
    6. t_thrd.security_ledger_cxt.prev_ExecutorEnd = (void *)ExecutorEnd_hook;
    7. ExecutorEnd_hook = ledger_ExecutorEnd;
    8. }
    • 生成 globalhash 规则
    全局区块表记录主要是生成 globalhash.

    调用过程:

    ledger_ExecutorEnd --> ledger_gchain_append --> set_gchain_comb_string

    --> get_next_g_blocknum

    --> gen_global_hash

    • set_gchain_comb_string,是一组字符串拼接成的:rel_name + nsp_name + query_string + rel_hash
    • get_next_g_blocknum,用全局变量 g_blocknum 保存
    • gen_global_hash,是的 set_gchain_comb_string 拼出来的串 + 上一条的 hash 值拼串然后再去 hash—— 区块链的基本原理
    1. bool gen_global_hash(hash32_t *hash_buffer, const char *info_string, bool exist, const hash32_t *prev_hash)
    2. {
    3. errno_t rc = EOK;
    4. int comb_strlen;
    5. char *comb_string = NULL;
    6. /*
    7. * Previous block not exists means current insertion block is genesis,
    8. * then we use global systable as origin combine string for globalhash
    9. * generation. If previous block exists, we will use previous global
    10. * hash as combine string to calculate globalhash.
    11. */
    12. if (!exist) {
    13. /* generate genesis block globalhash */
    14. comb_strlen = strlen(GCHAIN_NAME) + strlen(info_string) + 1;
    15. comb_string = (char *)palloc0(comb_strlen);
    16. rc = snprintf_s(comb_string, comb_strlen, comb_strlen - 1, "%s%s", GCHAIN_NAME, info_string);
    17. securec_check_ss(rc, "", "");
    18. } else {
    19. /* use previous globalhash and current block info to calculate globalhash. */
    20. char *pre_hash_str = DatumGetCString(DirectFunctionCall1(hash32out, HASH32GetDatum(prev_hash)));
    21. comb_strlen = strlen(pre_hash_str) + strlen(info_string) + 1;
    22. comb_string = (char *)palloc0(comb_strlen);
    23. rc = snprintf_s(comb_string, comb_strlen, comb_strlen - 1, "%s%s", info_string, pre_hash_str);
    24. securec_check_ss(rc, "", "");
    25. pfree_ext(pre_hash_str);
    26. }
    27. if (!pg_md5_binary(comb_string, comb_strlen - 1, hash_buffer->data)) {
    28. pfree(comb_string);
    29. ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Failed to generate globalhash, out of memory")));
    30. return false;
    31. }
    32. pfree(comb_string);
    33. return true;
    34. }
    • 在 src/gausskernel/runtime/executor/nodeModifyTable.cpp 中更新_hist 表的 hash 值。
    • 通过 set_user_tuple_hash 得到账本表 hash 列的值。
    1. /*
    2. * set_user_tuple_hash -- calculate and fill the hash attribute of user table's tuple.
    3. *
    4. * tup: row data of user table
    5. * rel: user table
    6. * hash_exists: whether tuple comes with tuplehash.
    7. *
    8. * Note: if hash_exists is true, we should recompute
    9. * tuple hash and compare with tuplehash of itself.
    10. */
    11. HeapTuple set_user_tuple_hash(HeapTuple tup, Relation rel, bool hash_exists)
    12. {
    13. uint64 row_hash = gen_user_tuple_hash(rel, tup);
    14. int hash_attrno = user_hash_attrno(rel->rd_att);
    15. if (hash_exists) {
    16. bool is_null;
    17. Datum hash = heap_getattr(tup, hash_attrno + 1, rel->rd_att, &is_null);
    18. if (is_null || row_hash != DatumGetUInt64(hash)) {
    19. ereport(ERROR, (errcode(ERRCODE_OPERATE_INVALID_PARAM), errmsg("Invalid tuple hash.")));
    20. }
    21. return tup;
    22. }
    23. Datum *values = NULL;
    24. bool *nulls = NULL;
    25. bool *replaces = NULL;
    26. /* Build modified tuple */
    27. int2 nattrs = RelationGetNumberOfAttributes(rel);
    28. values = (Datum*)palloc0(nattrs * sizeof(Datum));
    29. nulls = (bool*)palloc0(nattrs * sizeof(bool));
    30. replaces = (bool*)palloc0(nattrs * sizeof(bool));
    31. values[hash_attrno] = UInt64GetDatum(row_hash);
    32. replaces[hash_attrno] = true;
    33. HeapTuple newtup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls, replaces);
    34. pfree_ext(values);
    35. pfree_ext(nulls);
    36. pfree_ext(replaces);
    37. return newtup;
    38. }

    校验账本数据一致性

    官方文档

    数据库正常运行,并且对防篡改数据库执行了一系列增、删、改等操作,保证在查询时段内有账本操作记录结果产生。

    基本操作

    1、校验防篡改用户表 ledgernsp.usertable 与其对应的历史表是否一致。

    1. omm=# SELECT pg_catalog.ledger_hist_check('ledgernsp', 'usertable');
    2. ledger_hist_check
    3. -------------------
    4. t
    5. (1 row)
    • 校验用户权限 Only super user or audit admin have access right to blockchain nsp
    1. /* Only super user or audit admin have access right to blockchain nsp */
    2. if (nsp_oid == PG_BLOCKCHAIN_NAMESPACE) {
    3. return gs_blockchain_aclmask(roleid, mask);
    4. }
    • 校验历史表 hash 值

    is_hist_hash_identity --> get_usertable_hash_sum

    --> get_histtable_hash_sum

    1. /*
    2. * is_hist_hash_identity -- check whether user table hash and history table hash are equal
    3. *
    4. * relid: user table oid
    5. * res_hash: hash sum of history table
    6. */
    7. bool is_hist_hash_identity(Oid relid, uint64 *res_hash)
    8. {
    9. uint64 user_hash_sum;
    10. uint64 hist_hash_sum;
    11. char hist_name[NAMEDATALEN];
    12. char *rel_name = get_rel_name(relid);
    13. if (!get_hist_name(relid, rel_name, hist_name)) {
    14. ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("get hist table name failed.")));
    15. }
    16. Oid histoid = get_relname_relid(hist_name, PG_BLOCKCHAIN_NAMESPACE);
    17. if (!OidIsValid(histoid)) {
    18. ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find hist table of \"%s\".", rel_name)));
    19. }
    20. user_hash_sum = get_usertable_hash_sum(relid);
    21. hist_hash_sum = get_histtable_hash_sum(histoid);
    22. *res_hash = hist_hash_sum;
    23. return user_hash_sum == hist_hash_sum;
    24. }

    2、查询防篡改用户表 ledgernsp.usertable 与其对应的历史表以及全局区块表中关于该表的记录是否一致。

    1. omm=# SELECT pg_catalog.ledger_gchain_check('ledgernsp', 'usertable');
    2. ledger_gchain_check
    3. ---------------------
    4. t
    5. (1 row)
    • 校验是否为账本表 ledger_usertable_check
    • 校验用户权限 has_ledger_consistent_privilege
    • 校验历史表 hash 值 is_hist_hash_identity
    • 计算 / 校验全局表 hash get_gchain_relhash_sum
    1. /*
    2. * get_gchain_relhash_sum -- calculate relhash from gs_global_chain
    3. *
    4. * relid: user table oid
    5. */
    6. static uint64 get_gchain_relhash_sum(Oid relid)
    7. {
    8. uint64 relhash = 0;
    9. HeapTuple tuple = NULL;
    10. /* scan the gs_global_chain catalog by relid */
    11. Relation gchain_rel = heap_open(GsGlobalChainRelationId, AccessShareLock);
    12. Form_gs_global_chain rdata = NULL;
    13. TableScanDesc scan = heap_beginscan(gchain_rel, SnapshotNow, 0, NULL);
    14. while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) {
    15. rdata = (Form_gs_global_chain)GETSTRUCT(tuple);
    16. if (rdata == NULL || rdata->relid != relid) {
    17. continue;
    18. }
    19. relhash += rdata->relhash;
    20. }
    21. heap_endscan(scan);
    22. heap_close(gchain_rel, AccessShareLock);
    23. return relhash;
    24. }

    归档账本数据库

    官方文档

    前提条件:

    • 系统中需要有审计管理员或者具有审计管理员权限的角色。
    • 数据库正常运行,并且对防篡改数据库执行了一系列增、删、改等操作,保证在查询时段内有账本操作记录结果产生。
    • 数据库已经正确配置审计文件的存储路径 audit_directory。

    基本操作

    1、对指定用户历史表进行归档操作。

    1. omm=# SELECT pg_catalog.ledger_hist_archive('ledgernsp', 'usertable');
    2. ledger_hist_archive
    3. ---------------------
    4. t
    5. (1 row)
    6. omm=# SELECT * FROM blockchain.ledgernsp_usertable_hist;
    7. rec_num | hash_ins | hash_del | pre_hash
    8. ---------+------------------+------------------+----------------------------------
    9. 4 | e78e75b00d396899 | 84e8bfc3b974e9cf | 6475a497b7a272a92bab012d7f3d615b
    10. (1 row)

    主要步骤如下:

    1. Copy user history table.
    2. Do unify and truncate.
    3. sum all hash_ins and hash_del for unification.
    4. Do real truncate.heap_truncate_one_rel
    5. Do insertion for unified row.simple_heap_insert
    6. Flush history hash table cache.

    2、执行全局区块表导出操作

    1. omm=# SELECT * FROM gs_global_chain;
    2. blocknum | dbname | username | starttime | relid | relnsp | relname | relhash | globalhash |
    3. txcommand
    4. ----------+--------+----------+-------------------------------+-------+-----------+-----------+------------------+----------------------------------+----------------
    5. --------------------------------------------------------------
    6. 1 | omm | omm | 2022-09-17 13:59:37.84824+00 | 16404 | ledgernsp | usertable | a41714001181a294 | 83927d11ba1fd678e8f4b0723a9cd5f2 | INSERT INTO led
    7. gernsp.usertable VALUES(1, 'alex'), (2, 'bob'), (3, 'peter');
    8. 2 | omm | omm | 2022-09-17 13:59:51.723068+00 | 16404 | ledgernsp | usertable | b3a9ed0755131181 | b5ee73b6c20c817230182f6373c78e20 | UPDATE ledgerns
    9. p.usertable SET name = 'bob2' WHERE id = 2;
    10. 3 | omm | omm | 2022-09-17 13:59:58.159596+00 | 16404 | ledgernsp | usertable | 0ae4b4e4ed2fcab5 | 0cc9938cf7f1ed7f7f1a03c29954380a | DELETE FROM led
    11. gernsp.usertable WHERE id = 3;
    12. (3 rows)
    13. omm=# SELECT pg_catalog.ledger_gchain_archive();
    14. ledger_gchain_archive
    15. -----------------------
    16. t
    17. (1 row)
    18. omm=# SELECT * FROM gs_global_chain;
    19. blocknum | dbname | username | starttime | relid | relnsp | relname | relhash | globalhash | txcommand
    20. ----------+--------+----------+------------------------------+-------+-----------+-----------+------------------+----------------------------------+-----------
    21. 2 | omm | omm | 2022-09-17 13:59:37.84824+00 | 16404 | ledgernsp | usertable | 62a5b5ec53c47eca | 7252d09679b0b3836a2e63da17284ad5 | Archived.
    22. (1 row)

    gs_global_chain 主要处理流程:

    1. Init and prepare bak dictionary.
    2. Using CopyStmt to copy global chain.
    3. Do unify and truncate.
    4. Using hash table to do unify, each hash_entry refers to one relid informations.
    5. Split gs_global_chain by relid, and accumulate rel_hash to a new record for each rel.
    6. Do rel truncate.
    7. Insert newest record to gchain order by relid.
    8. Flush global_hash cache.

    修复账本数据库

    官方文档

    前提条件:

    • 系统中需要有审计管理员或者具有审计管理员权限的角色。
    • 数据库正常运行,并且对防篡改数据库执行了一系列增、删、改等操作,保证在查询时段内有账本操作记录结果产生。

    基本操作

    1、执行历史表修复操作

    1. omm=# select * from blockchain.ledgernsp_usertable_hist;
    2. rec_num | hash_ins | hash_del | pre_hash
    3. ---------+------------------+------------------+----------------------------------
    4. 4 | e78e75b00d396899 | 84e8bfc3b974e9cf | 6475a497b7a272a92bab012d7f3d615b
    5. (1 row)
    6. omm=# SELECT pg_catalog.ledger_hist_repair('ledgernsp', 'usertable');
    7. ledger_hist_repair
    8. --------------------
    9. 0000000000000000
    10. (1 row)

    [drawio] (rHmeQ8HWKS_RFXgP-oTUZINZguxBYqh2IV64Y0j5TAA.svg)

    2、执行全局区块表修复操作

    1. omm=# select * from gs_global_chain ;
    2. blocknum | dbname | username | starttime | relid | relnsp | relname | relhash | globalhash | txcommand
    3. ----------+--------+----------+------------------------------+-------+-----------+-----------+------------------+----------------------------------+-----------
    4. 2 | omm | omm | 2022-09-17 13:59:37.84824+00 | 16404 | ledgernsp | usertable | 62a5b5ec53c47eca | 7252d09679b0b3836a2e63da17284ad5 | Archived.
    5. (1 row)
    6. omm=# SELECT pg_catalog.ledger_gchain_repair('ledgernsp', 'usertable');
    7. ledger_gchain_repair
    8. ----------------------
    9. 62a5b5ec53c47eca
    10. (1 row)

    首先判断用户权限,之后通过 get_gchain_relhash_sum 函数计算 relhash 字段

    1. /*
    2. * get_gchain_relhash_sum -- calculate relhash from gs_global_chain
    3. *
    4. * relid: user table oid
    5. */
    6. static uint64 get_gchain_relhash_sum(Oid relid)
    7. {
    8. uint64 relhash = 0;
    9. HeapTuple tuple = NULL;
    10. /* scan the gs_global_chain catalog by relid */
    11. Relation gchain_rel = heap_open(GsGlobalChainRelationId, AccessShareLock);
    12. Form_gs_global_chain rdata = NULL;
    13. TableScanDesc scan = heap_beginscan(gchain_rel, SnapshotNow, 0, NULL);
    14. while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) {
    15. rdata = (Form_gs_global_chain)GETSTRUCT(tuple);
    16. if (rdata == NULL || rdata->relid != relid) {
    17. continue;
    18. }
    19. relhash += rdata->relhash;
    20. }
    21. heap_endscan(scan);
    22. heap_close(gchain_rel, AccessShareLock);
    23. return relhash;
    24. }

    主要是计算并修复 gs_global_chain 中的 relhash 字段。

    总结

    账本数据库其实并不像我们想象的那么复制,实际上就是利用了区块链的最基本的原理,即当前记录的特征值 + 上一条记录特征值的 hash 值,再进行 hash。下一条与上一条记录具有数据关联性,形成 “链” 的结构,如果篡改了其中的数据,则会导致 “链” 断开,导致不能与后面数据记录形成 hash 关联。_hist 表记录了用户表每一步数据变化的过程,gs_global_chain 表记录了所有防篡改模式下对用户表的操作记录。用户表结合_hist 和 global 表就能完整记录和校验。

  • 相关阅读:
    React报错之react component changing uncontrolled input
    Linux基础命令行操作
    Hive常用函数大全 正则表达式大全
    一次人脸识别ViewFaceCore使用的经验分享,看我把门店淘汰下来的POS机改成了人脸考勤机
    【第2章 Node.js基础】2.2 Node.js回调函数
    git的基本使用
    数据结构与算法之堆: Leetcode 313. 超级丑数 (Typescript版)
    《算法导论》第13章-红黑树 13.1-红黑树的性质 13.2-旋转
    开源许可证GPL、BSD、MIT、Mozilla、Apache和LGPL的区别
    什么是web3.0?
  • 原文地址:https://blog.csdn.net/u012181546/article/details/127964136