mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 18:32:23 +00:00
Add transaction commitment precompile
This commit is contained in:
parent
758f77be3e
commit
42a3c915e4
10 changed files with 1104 additions and 55 deletions
|
|
@ -105,7 +105,7 @@ var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{
|
|||
}
|
||||
|
||||
var PrecompiledContractsMina = map[common.Address]PrecompiledContract{
|
||||
common.BytesToAddress([]byte{0x50}): &MinaPoseidon{},
|
||||
common.BytesToAddress([]byte{0x50}): &MinaHasher{},
|
||||
common.BytesToAddress([]byte{0x51}): &MinaSigner{},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ var allPrecompiles = map[common.Address]PrecompiledContract{
|
|||
common.BytesToAddress([]byte{16}): &bls12381Pairing{},
|
||||
common.BytesToAddress([]byte{17}): &bls12381MapG1{},
|
||||
common.BytesToAddress([]byte{18}): &bls12381MapG2{},
|
||||
common.BytesToAddress([]byte{0x50}): &MinaPoseidon{},
|
||||
common.BytesToAddress([]byte{0x50}): &MinaHasher{},
|
||||
common.BytesToAddress([]byte{0x51}): &MinaSigner{},
|
||||
}
|
||||
|
||||
|
|
@ -313,7 +313,7 @@ func TestPrecompiledBLS12381G2MultiExp(t *testing.T) { testJson("blsG2MultiExp",
|
|||
func TestPrecompiledBLS12381Pairing(t *testing.T) { testJson("blsPairing", "10", t) }
|
||||
func TestPrecompiledBLS12381MapG1(t *testing.T) { testJson("blsMapG1", "11", t) }
|
||||
func TestPrecompiledBLS12381MapG2(t *testing.T) { testJson("blsMapG2", "12", t) }
|
||||
func TestPrecompiledMinaPoseidon(t *testing.T) { testJson("minaPoseidon", "50", t) }
|
||||
func TestPrecompiledMinaHasher(t *testing.T) { testJson("minaHasher", "50", t) }
|
||||
func TestPrecompiledMinaSigner(t *testing.T) { testJson("minaSigner", "51", t) }
|
||||
|
||||
func BenchmarkPrecompiledBLS12381G1Add(b *testing.B) { benchJson("blsG1Add", "0a", b) }
|
||||
|
|
@ -325,7 +325,7 @@ func BenchmarkPrecompiledBLS12381G2MultiExp(b *testing.B) { benchJson("blsG2Mult
|
|||
func BenchmarkPrecompiledBLS12381Pairing(b *testing.B) { benchJson("blsPairing", "10", b) }
|
||||
func BenchmarkPrecompiledBLS12381MapG1(b *testing.B) { benchJson("blsMapG1", "11", b) }
|
||||
func BenchmarkPrecompiledBLS12381MapG2(b *testing.B) { benchJson("blsMapG2", "12", b) }
|
||||
func BenchmarkPrecompiledMinaPoseidon(b *testing.B) { benchJson("minaPoseidon", "50", b) }
|
||||
func BenchmarkPrecompiledMinaHasher(b *testing.B) { benchJson("minaHasher", "50", b) }
|
||||
func BenchmarkPrecompiledMinaSigner(b *testing.B) { benchJson("minaSigner", "51", b) }
|
||||
|
||||
// Failure tests
|
||||
|
|
@ -338,7 +338,7 @@ func TestPrecompiledBLS12381G2MultiExpFail(t *testing.T) { testJsonFail("blsG2Mu
|
|||
func TestPrecompiledBLS12381PairingFail(t *testing.T) { testJsonFail("blsPairing", "10", t) }
|
||||
func TestPrecompiledBLS12381MapG1Fail(t *testing.T) { testJsonFail("blsMapG1", "11", t) }
|
||||
func TestPrecompiledBLS12381MapG2Fail(t *testing.T) { testJsonFail("blsMapG2", "12", t) }
|
||||
func TestPrecompiledMinaPoseidonFail(t *testing.T) { testJsonFail("minaPoseidon", "50", t) }
|
||||
func TestPrecompiledMinaHasherFail(t *testing.T) { testJsonFail("minaHasher", "50", t) }
|
||||
func TestPrecompiledMinaSignerFail(t *testing.T) { testJsonFail("minaSigner", "51", t) }
|
||||
|
||||
func loadJson(name string) ([]precompiledTest, error) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@ package vm
|
|||
// HashParameter hashParameter,
|
||||
// bytes32[] memory fields
|
||||
// ) external view returns (bytes32);
|
||||
//
|
||||
// function transasctionCommitment(
|
||||
// bytes memory zkappCommand
|
||||
// ) external view returns (bytes32);
|
||||
// }
|
||||
//
|
||||
// interface ISigner {
|
||||
|
|
@ -42,6 +46,7 @@ import (
|
|||
var sol_bool, _ = abi.NewType("bool", "", nil)
|
||||
var sol_uint8, _ = abi.NewType("uint8", "", nil)
|
||||
var sol_string, _ = abi.NewType("string", "", nil)
|
||||
var sol_bytes, _ = abi.NewType("bytes", "", nil)
|
||||
var sol_bytes32, _ = abi.NewType("bytes32", "", nil)
|
||||
var sol_bytes32Arr, _ = abi.NewType("bytes32[]", "", nil)
|
||||
|
||||
|
|
@ -62,22 +67,13 @@ var (
|
|||
errMinaCallingRustLibFailed = errors.New("calling rust library failed")
|
||||
)
|
||||
|
||||
type MinaPoseidon struct{}
|
||||
type MinaHasher struct{}
|
||||
|
||||
func (c *MinaPoseidon) RequiredGas(input []byte) uint64 {
|
||||
func (c *MinaHasher) RequiredGas(input []byte) uint64 {
|
||||
return 1000
|
||||
}
|
||||
|
||||
// 0x1f831f84
|
||||
var poseidonHashSignature = crypto.Keccak256([]byte("poseidonHash(uint8,bytes32[])"))[:4]
|
||||
|
||||
func (c *MinaPoseidon) Run(input []byte) ([]byte, error) {
|
||||
if len(input) < 4 || !bytes.Equal(input[:4], poseidonHashSignature) {
|
||||
return packErr("Invalid signature"), errMinaInvalidSignature
|
||||
}
|
||||
|
||||
calldata := input[4:]
|
||||
|
||||
func poseidonHash(calldata []byte) ([]byte, error) {
|
||||
unpacked, err := (abi.Arguments{{
|
||||
Type: sol_uint8}, // hashParameter
|
||||
{Type: sol_bytes32Arr}, // fields
|
||||
|
|
@ -111,6 +107,53 @@ func (c *MinaPoseidon) Run(input []byte) ([]byte, error) {
|
|||
return output_buffer[:], nil
|
||||
}
|
||||
|
||||
func transasctionCommitment(calldata []byte) ([]byte, error) {
|
||||
unpacked, err := (abi.Arguments{{Type: sol_bytes}}).Unpack(calldata)
|
||||
|
||||
if err != nil {
|
||||
return packErr("Unable to unpack calldata"), err
|
||||
}
|
||||
|
||||
zkappCommand := unpacked[0].([]uint8)
|
||||
|
||||
output_buffer := [32]byte{}
|
||||
|
||||
if !C.transaction_commitment(
|
||||
(*C.uint8_t)(&zkappCommand[0]),
|
||||
C.uintptr_t(len(zkappCommand)),
|
||||
(*C.uint8_t)(&output_buffer[0]),
|
||||
) {
|
||||
return packErr("Calling Transaction Commitment failed"), errMinaCallingRustLibFailed
|
||||
}
|
||||
|
||||
return output_buffer[:], nil
|
||||
}
|
||||
|
||||
// 0x1f831f84
|
||||
var poseidonHashSignature = crypto.Keccak256([]byte("poseidonHash(uint8,bytes32[])"))[:4]
|
||||
|
||||
// 0x1a7f99b6
|
||||
var transasctionCommitmentSignature = crypto.Keccak256([]byte("transasctionCommitment(bytes)"))[:4]
|
||||
|
||||
func (c *MinaHasher) Run(input []byte) ([]byte, error) {
|
||||
if len(input) < 4 {
|
||||
return packErr("Invalid signature"), errMinaInvalidSignature
|
||||
}
|
||||
|
||||
signature := input[:4]
|
||||
calldata := input[4:]
|
||||
|
||||
if bytes.Equal(signature, poseidonHashSignature) {
|
||||
return poseidonHash(calldata)
|
||||
}
|
||||
|
||||
if bytes.Equal(signature, transasctionCommitmentSignature) {
|
||||
return transasctionCommitment(calldata)
|
||||
}
|
||||
|
||||
return packErr("Invalid signature"), errMinaInvalidSignature
|
||||
}
|
||||
|
||||
type MinaSigner struct{}
|
||||
|
||||
func (c *MinaSigner) RequiredGas(input []byte) uint64 {
|
||||
|
|
|
|||
|
|
@ -40,5 +40,12 @@
|
|||
"Name": "vector 6",
|
||||
"Gas": 1000,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "1a7f99b6000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001ed01756ae9c3d6720d4e0404bb335498e59370db54a717b4088898483bfb3f1e850a00000000d290f924705fb714e91fedb9bed77e85bce8f5d932c3f4d692b20e4c3e5f9a3343c2baffce9ab0c2391e2f3de8ac891633338d827e6fd4f269331c248029b10601756ae9c3d6720d4e0404bb335498e59370db54a717b4088898483bfb3f1e850a000100000000000000000000000000000000000000000000000000000000000000010101010101010100010101010101010a01000000000000000000000000000000000000000000000000000000000000000000000001010100010101010101010101010101010102010100000001d290f924705fb714e91fedb9bed77e85bce8f5d932c3f4d692b20e4c3e5f9a3343c2baffce9ab0c2391e2f3de8ac891633338d827e6fd4f269331c248029b10600014ff72ff7bf9824551a1b2bd5a2bebdb0234732f6f967f5d727fd80864a35a810010100000000000000000000000000000000000000000000000000000000000000010101010101010100010101010101010a0000000000000000000000000000000000000000000000000000000000000000000000000101010001010101010101010101010101010201000000020200000000220100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"Expected": "9484a9d4aac1d64a958aa88dd1943a87216739980701a4c8e3963633a678603e",
|
||||
"Name": "transaction commitment",
|
||||
"Gas": 1000,
|
||||
"NoBenchmark": false
|
||||
}
|
||||
]
|
||||
919
mina/Cargo.lock
generated
919
mina/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -9,9 +9,12 @@ edition = "2021"
|
|||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
mina-hasher = { git = "https://github.com/o1-labs/proof-systems", tag = "0.1.0", version = "0.1.0" }
|
||||
mina-signer = { git = "https://github.com/o1-labs/proof-systems", tag = "0.1.0", version = "0.1.0" }
|
||||
o1-utils = { git = "https://github.com/o1-labs/proof-systems", tag = "0.1.0", version = "0.1.0" }
|
||||
mina-tree = { version = "0.1.0", path = "../../ledger" }
|
||||
mina-p2p-messages = { git = "https://github.com/openmina/mina-p2p-messages-rs", branch = "signed-amount", features = ["hashing"] }
|
||||
mina-hasher = { git = "https://github.com/openmina/proof-systems", branch = "ledger-newtypes" }
|
||||
mina-signer = { git = "https://github.com/openmina/proof-systems", branch = "ledger-newtypes" }
|
||||
o1-utils = { git = "https://github.com/openmina/proof-systems", branch = "ledger-newtypes" }
|
||||
binprot = { git = "https://github.com/openmina/binprot-rs", rev = "ead5ba916725abf8ab212b89985b47c79d1a006a" }
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = "0.24.3"
|
||||
|
|
@ -20,3 +23,15 @@ cbindgen = "0.24.3"
|
|||
num-bigint = "0.4.3"
|
||||
serde = "1.0.160"
|
||||
serde_json = "1.0.96"
|
||||
|
||||
[patch.crates-io]
|
||||
ark-ff = { git = "https://github.com/openmina/algebra", branch = "openmina" }
|
||||
ark-ec = { git = "https://github.com/openmina/algebra", branch = "openmina" }
|
||||
ark-poly = { git = "https://github.com/openmina/algebra", branch = "openmina" }
|
||||
ark-serialize = { git = "https://github.com/openmina/algebra", branch = "openmina" }
|
||||
|
||||
[patch.'https://github.com/o1-labs/proof-systems']
|
||||
mina-hasher = { git = "https://github.com/openmina/proof-systems", branch = "ledger-newtypes" }
|
||||
mina-signer = { git = "https://github.com/openmina/proof-systems", branch = "ledger-newtypes" }
|
||||
mina-curves = { git = "https://github.com/openmina/proof-systems", branch = "ledger-newtypes" }
|
||||
o1-utils = { git = "https://github.com/openmina/proof-systems", branch = "ledger-newtypes" }
|
||||
|
|
|
|||
|
|
@ -24,3 +24,10 @@ bool verify(uint8_t network_id,
|
|||
const uint8_t *field_ptr,
|
||||
uintptr_t field_len,
|
||||
bool *output_ptr);
|
||||
|
||||
/**
|
||||
* * # Safety * this functions accepts raw pointer from golang
|
||||
*/
|
||||
bool transaction_commitment(const uint8_t *zkapp_command_ptr,
|
||||
uintptr_t zkapp_command_len,
|
||||
uint8_t *output_ptr);
|
||||
|
|
|
|||
|
|
@ -2,8 +2,13 @@ mod mina;
|
|||
|
||||
use std::array::TryFromSliceError;
|
||||
|
||||
use mina::{HashParameter, Message};
|
||||
use binprot::BinProtRead;
|
||||
use mina::{full_transaction_commitment, HashParameter, Message};
|
||||
use mina_p2p_messages::v2::{
|
||||
MinaBaseUserCommandStableV2, MinaBaseZkappCommandTStableV1WireStableV1,
|
||||
};
|
||||
use mina_signer::{BaseField, CurvePoint, PubKey, ScalarField, Signature};
|
||||
use mina_tree::scan_state::transaction_logic::zkapp_command::ZkAppCommand;
|
||||
use o1_utils::FieldHelpers;
|
||||
|
||||
pub const FIELD_SIZE: usize = 32;
|
||||
|
|
@ -139,6 +144,44 @@ pub unsafe extern "C" fn verify(
|
|||
true
|
||||
}
|
||||
|
||||
/**
|
||||
* # Safety
|
||||
* this functions accepts raw pointer from golang
|
||||
*/
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn transaction_commitment(
|
||||
zkapp_command_ptr: *const u8,
|
||||
zkapp_command_len: usize,
|
||||
output_ptr: *mut u8, // 32 bytes
|
||||
) -> bool {
|
||||
if zkapp_command_ptr.is_null() || zkapp_command_len == 0 || output_ptr.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut raw_zkapp_command =
|
||||
unsafe { std::slice::from_raw_parts(zkapp_command_ptr, zkapp_command_len * FIELD_SIZE) };
|
||||
|
||||
let p2p_user_command = match MinaBaseUserCommandStableV2::binprot_read(&mut raw_zkapp_command) {
|
||||
Ok(p2p_user_command) => p2p_user_command,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
let p2p_zkapp_command: MinaBaseZkappCommandTStableV1WireStableV1 = match p2p_user_command {
|
||||
MinaBaseUserCommandStableV2::ZkappCommand(zkapp_command) => zkapp_command,
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
let zkapp_command = ZkAppCommand::from(&p2p_zkapp_command);
|
||||
|
||||
let commitment = full_transaction_commitment(&zkapp_command);
|
||||
|
||||
let output = unsafe { std::slice::from_raw_parts_mut(output_ptr, FIELD_SIZE) };
|
||||
|
||||
output.copy_from_slice(&commitment.0.to_bytes());
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
|
@ -266,6 +309,56 @@ mod tests {
|
|||
0,
|
||||
std::ptr::null_mut()
|
||||
));
|
||||
assert!(!transaction_commitment(
|
||||
std::ptr::null(),
|
||||
0,
|
||||
std::ptr::null_mut()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction_commitment() {
|
||||
let expected_commitment = BaseField::from_str(
|
||||
"28213846620687386246595472357080373779104425260189267168855581893969578656916",
|
||||
)
|
||||
.expect("valid field element");
|
||||
|
||||
let zkapp_command_bytes: Vec<u8> = vec![
|
||||
1, 117, 106, 233, 195, 214, 114, 13, 78, 4, 4, 187, 51, 84, 152, 229, 147, 112, 219,
|
||||
84, 167, 23, 180, 8, 136, 152, 72, 59, 251, 63, 30, 133, 10, 0, 0, 0, 0, 210, 144, 249,
|
||||
36, 112, 95, 183, 20, 233, 31, 237, 185, 190, 215, 126, 133, 188, 232, 245, 217, 50,
|
||||
195, 244, 214, 146, 178, 14, 76, 62, 95, 154, 51, 67, 194, 186, 255, 206, 154, 176,
|
||||
194, 57, 30, 47, 61, 232, 172, 137, 22, 51, 51, 141, 130, 126, 111, 212, 242, 105, 51,
|
||||
28, 36, 128, 41, 177, 6, 1, 117, 106, 233, 195, 214, 114, 13, 78, 4, 4, 187, 51, 84,
|
||||
152, 229, 147, 112, 219, 84, 167, 23, 180, 8, 136, 152, 72, 59, 251, 63, 30, 133, 10,
|
||||
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 10, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
|
||||
1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0, 1, 210, 144, 249,
|
||||
36, 112, 95, 183, 20, 233, 31, 237, 185, 190, 215, 126, 133, 188, 232, 245, 217, 50,
|
||||
195, 244, 214, 146, 178, 14, 76, 62, 95, 154, 51, 67, 194, 186, 255, 206, 154, 176,
|
||||
194, 57, 30, 47, 61, 232, 172, 137, 22, 51, 51, 141, 130, 126, 111, 212, 242, 105, 51,
|
||||
28, 36, 128, 41, 177, 6, 0, 1, 79, 247, 47, 247, 191, 152, 36, 85, 26, 27, 43, 213,
|
||||
162, 190, 189, 176, 35, 71, 50, 246, 249, 103, 245, 215, 39, 253, 128, 134, 74, 53,
|
||||
168, 16, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 10, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 0, 0, 0, 2, 2, 0, 0,
|
||||
0, 0, 34, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
];
|
||||
|
||||
let mut output = [0u8; 32];
|
||||
|
||||
assert!(unsafe {
|
||||
transaction_commitment(
|
||||
zkapp_command_bytes.as_ptr(),
|
||||
zkapp_command_bytes.len(),
|
||||
output.as_mut_ptr(),
|
||||
)
|
||||
});
|
||||
|
||||
assert_eq!(BaseField::from_bytes(&output).unwrap(), expected_commitment);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use mina_hasher::{DomainParameter, Hashable, Hasher, ROInput};
|
||||
use mina_signer::{BaseField, PubKey, Signature, Signer};
|
||||
use mina_tree::scan_state::transaction_logic::zkapp_command::{AccountUpdate, ZkAppCommand};
|
||||
use o1_utils::{field_helpers::FieldHelpersError, FieldHelpers};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -8,6 +9,7 @@ pub enum HashParameter {
|
|||
Mainnet = 0x00,
|
||||
Testnet = 0x01,
|
||||
Empty = 0x02,
|
||||
TransactionCommitment = 0x03,
|
||||
}
|
||||
|
||||
impl From<HashParameter> for u8 {
|
||||
|
|
@ -51,11 +53,32 @@ impl Hashable for Message {
|
|||
match network_id {
|
||||
HashParameter::Mainnet => "MinaSignatureMainnet".to_string().into(),
|
||||
HashParameter::Testnet => "CodaSignature".to_string().into(),
|
||||
HashParameter::TransactionCommitment => "MinaAcctUpdateCons".to_string().into(),
|
||||
HashParameter::Empty => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TransactionCommitment(pub BaseField);
|
||||
|
||||
impl TransactionCommitment {
|
||||
fn create(account_updates_hash: BaseField) -> Self {
|
||||
Self(account_updates_hash)
|
||||
}
|
||||
|
||||
fn create_complete(&self, memo_hash: BaseField, fee_payer_hash: BaseField) -> Self {
|
||||
let mut hasher =
|
||||
mina_hasher::create_kimchi::<Message>(HashParameter::TransactionCommitment);
|
||||
|
||||
let msg = &Message {
|
||||
fields: vec![memo_hash, fee_payer_hash, self.0],
|
||||
};
|
||||
|
||||
TransactionCommitment(hasher.hash(msg))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poseidon(msg: &Message, network_id: HashParameter) -> BaseField {
|
||||
let mut hasher = mina_hasher::create_kimchi::<Message>(network_id);
|
||||
|
||||
|
|
@ -72,3 +95,13 @@ pub fn verify(
|
|||
|
||||
signer.verify(signature, pubkey, msg)
|
||||
}
|
||||
|
||||
pub fn full_transaction_commitment(zkapp_command: &ZkAppCommand) -> TransactionCommitment {
|
||||
let memo_hash = zkapp_command.memo.hash();
|
||||
let fee_payer_hash = AccountUpdate::of_fee_payer(zkapp_command.fee_payer.clone()).digest();
|
||||
let account_updates_hash = zkapp_command.account_updates_hash();
|
||||
|
||||
let txn_commitment = TransactionCommitment::create(account_updates_hash);
|
||||
|
||||
txn_commitment.create_complete(memo_hash, fee_payer_hash)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue