我正在使用 MySQL 作为数据库,在 Rust 中开发区块链实现。我有两个表:块和交易。交易通过外键(block_index)与块关联。
CREATE TABLE blocks (
block_index BIGINT NOT NULL PRIMARY KEY,
previous_hash VARCHAR(255) NOT NULL,
block_hash VARCHAR(255) NOT NULL,
validator_signature VARCHAR(255) NOT NULL,
timestamp DATETIME NOT NULL
);
CREATE TABLE transactions (
transaction_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
block_index BIGINT NOT NULL,
file_hash VARCHAR(255) NOT NULL,
uploader_id BIGINT,
uploader_name VARCHAR(255) NOT NULL,
transaction_checksum VARCHAR(255) NOT NULL,
timestamp DATETIME NOT NULL,
FOREIGN KEY (block_index) REFERENCES blocks(block_index)
);
#[derive(Debug, Clone)]
pub struct Transaction {
pub transaction_id: u64,
pub block_index: Option<u64>, // Associated block index
pub file_hash: String,
pub uploader_id: Option<u64>,
pub uploader_name: String,
pub transaction_checksum: String,
pub timestamp: chrono::DateTime<chrono::Utc>,
}
pub async fn add_transaction(
&mut self,
transaction: Transaction,
pool: &mysql_async::Pool,
) -> Result<(), mysql_async::Error> {
if transaction.block_index.is_none() {
return Err(mysql_async::Error::Other(Box::new(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Transaction must have a valid block_index",
))));
}
let mut conn = pool.get_conn().await?;
conn.exec_drop(
r"INSERT INTO transactions (
transaction_id, block_index, file_hash, uploader_id, uploader_name,
transaction_checksum, timestamp
) VALUES (
:transaction_id, :block_index, :file_hash, :uploader_id, :uploader_name,
:transaction_checksum, :timestamp
)",
mysql_async::params! {
"transaction_id" => transaction.transaction_id,
"block_index" => transaction.block_index.unwrap(),
"file_hash" => transaction.file_hash,
"uploader_id" => transaction.uploader_id,
"uploader_name" => transaction.uploader_name,
"transaction_checksum" => transaction.transaction_checksum,
"timestamp" => transaction.timestamp.to_string(),
},
).await?;
Ok(())
}
#[tokio::test]
async fn test_add_transaction() {
let database_url = "mysql://root:password@localhost:3306/test_public_blockchain";
let pool = mysql_async::Pool::new(database_url);
let mut conn = pool.get_conn().await.unwrap();
conn.exec_drop("DELETE FROM transactions", ()).await.unwrap();
conn.exec_drop("DELETE FROM blocks", ()).await.unwrap();
conn.exec_drop(
r"INSERT INTO blocks (block_index, previous_hash, block_hash, validator_signature,
timestamp)
VALUES (1, '0', 'genesis_hash', 'validator1', NOW())",
(),
).await.unwrap();
let transaction = Transaction {
transaction_id: 1,
block_index: Some(1),
file_hash: "test_hash".to_string(),
uploader_id: Some(1),
uploader_name: "Test Uploader".to_string(),
transaction_checksum: "checksum".to_string(),
timestamp: chrono::Utc::now(),
};
let mut blockchain = Blockchain::new(Block::new(0, "0".to_string(), vec![],
"validator1".to_string()), vec![]);
blockchain.add_transaction(transaction.clone(), &pool).await.unwrap();
}
当我运行测试时,出现以下错误:
Server(ServerError { code: 1048, message: "Column 'block_index' cannot be null", state:
"23000" })
我在 SQL 查询之前验证了调试日志中的
block_index
值。
我已经确认
block_index = 1
存在于块表中
我手动运行了查询,直接在 MySQL 中执行就可以了
我在插入数据之前添加了
println!
来保证数据,以确保没有任何内容为空或特定的block_index不为空,我已将block_index硬编码为1,但仍然收到它为空的错误,我尝试过禁用外键看看这是否是问题所在,但仍然出现相同的错误
问题:
为什么 block_index 在 Rust 代码中设置为
NULL
时,在查询执行中却显示为 Some(1)
?如何使用 mysql_async crate 在 Rust 中解决这个问题?
我相信我已经明白了。我添加了创世块,但我错误地尝试将交易添加到创世块,而不是将它们添加到下一个块,下一个块将是创世块之后的第一个块。问题是我的下一个块设置为索引 1 和先前的哈希值 0。一旦我切换到使用块索引 2 和创世块的先前哈希值,我的所有测试都通过了。