neo_solidity/cli/bytecode/bytecode_builtins/
syscalls.rs1fn is_void_syscall(name: &str) -> bool {
2 matches!(
3 name,
4 "System.Storage.Put"
5 | "System.Storage.Delete"
6 | "System.Storage.Local.Put"
7 | "System.Storage.Local.Delete"
8 | "System.Runtime.Notify"
9 | "System.Runtime.Log"
10 | "System.Runtime.BurnGas"
11 | "System.Runtime.LoadScript"
12 )
13}
14
15fn emit_native_contract_call(
16 bytecode: &mut Vec<u8>,
17 contract: ir::NativeContract,
18 method: &str,
19 arg_count: usize,
20 use_callt: bool,
21 token_patches: &mut Vec<MethodTokenPatch>,
22) {
23 let method = match (contract, method) {
25 (ir::NativeContract::ContractManagement, "listContracts") => "getContractHashes",
26 _ => method,
27 };
28
29 let call_flags = native_method_call_flags(contract, method);
30
31 if use_callt {
32 if let Ok(params) = u16::try_from(arg_count) {
33 if !method.starts_with('_') && method.len() <= neo_solidity::neo::MAX_TOKEN_METHOD_LENGTH
34 {
35 if let Some(has_return_value) = native_method_has_return_value(contract, method) {
36 bytecode.push(0x37); let position = bytecode.len();
38 bytecode.extend_from_slice(&[0, 0]); token_patches.push(MethodTokenPatch {
41 position,
42 token: MethodTokenKey {
43 hash: native_contract_hash(contract),
44 method: method.to_string(),
45 parameters_count: params,
46 has_return_value,
47 call_flags,
48 },
49 });
50
51 if !has_return_value {
52 bytecode.push(0x11); }
56 return;
57 }
58 }
59 }
60 }
61
62 let arg_count_bigint = BigInt::from(arg_count);
64 push_integer_bigint(bytecode, &arg_count_bigint);
65 bytecode.push(0xC0); if arg_count > 1 {
69 bytecode.push(0x4A); bytecode.push(0xD1); }
72
73 push_integer_bigint(bytecode, &BigInt::from(call_flags));
75 push_data(bytecode, method.as_bytes());
76 let hash = native_contract_hash(contract);
77 push_data(bytecode, &hash);
78
79 emit_syscall(bytecode, "System.Contract.Call");
82}
83
84fn native_method_call_flags(contract: ir::NativeContract, method: &str) -> u8 {
85 match native_method_is_mutating(contract, method) {
86 Some(true) => CALLFLAGS_ALL,
87 Some(false) => CALLFLAGS_READ_ONLY,
88 None => CALLFLAGS_ALL,
89 }
90}
91
92fn native_method_is_mutating(contract: ir::NativeContract, method: &str) -> Option<bool> {
93 use ir::NativeContract::*;
94
95 let is_mutating = match (contract, method) {
96 (Neo, "transfer" | "vote" | "registerCandidate" | "unregisterCandidate" | "setGasPerBlock"
97 | "setRegisterPrice") => true,
98 (Gas, "transfer") => true,
99 (ContractManagement, "deploy" | "update" | "destroy" | "setMinimumDeploymentFee") => true,
100 (Policy, "setFeePerByte" | "setExecFeeFactor" | "setStoragePrice" | "setAttributeFee"
101 | "setMillisecondsPerBlock" | "setMaxValidUntilBlockIncrement" | "setMaxTraceableBlocks"
102 | "blockAccount" | "unblockAccount" | "recoverFund"
103 | "setWhitelistFeeContract" | "removeWhitelistFeeContract") => true,
104 (Oracle, "request" | "setPrice" | "finish") => true,
105 (RoleManagement, "designateAsRole") => true,
106 (Notary, "lockDepositUntil" | "withdraw" | "setMaxNotValidBeforeDelta" | "onNEP17Payment") => true,
107 _ => {
108 if native_method_has_return_value(contract, method).is_some() {
109 return Some(false);
110 }
111 return None;
112 }
113 };
114
115 Some(is_mutating)
116}
117
118fn native_method_has_return_value(contract: ir::NativeContract, method: &str) -> Option<bool> {
119 use ir::NativeContract::*;
120
121 let has_return = match (contract, method) {
122 (StdLib, "serialize" | "deserialize" | "jsonSerialize" | "jsonDeserialize" | "base64Encode"
123 | "base64Decode" | "base64UrlEncode" | "base64UrlDecode" | "base58Encode" | "base58Decode"
124 | "base58CheckEncode" | "base58CheckDecode" | "hexEncode" | "hexDecode" | "itoa" | "atoi"
125 | "memoryCompare" | "memorySearch" | "stringSplit" | "strLen") => true,
126 (CryptoLib, "sha256" | "ripemd160" | "keccak256" | "murmur32" | "recoverSecp256K1"
127 | "verifyWithECDsa" | "verifyWithEd25519" | "bls12381Serialize" | "bls12381Deserialize"
128 | "bls12381Equal" | "bls12381Add" | "bls12381Mul" | "bls12381Pairing") => true,
129 (Ledger, "currentIndex" | "getBlock" | "getTransaction" | "getTransactionHeight"
130 | "getTransactionFromBlock") => true,
131 (Neo, "symbol" | "decimals" | "totalSupply" | "balanceOf" | "transfer" | "vote" | "getCandidates"
132 | "registerCandidate" | "unregisterCandidate" | "getGasPerBlock" | "getRegisterPrice"
133 | "getAccountState" | "unclaimedGas" | "getCandidateVote" | "getCommittee"
134 | "getCommitteeAddress" | "getNextBlockValidators" | "getAllCandidates") => true,
135 (Neo, "setGasPerBlock" | "setRegisterPrice") => false,
136 (Gas, "symbol" | "decimals" | "totalSupply" | "balanceOf" | "transfer") => true,
137 (ContractManagement, "deploy" | "getContract" | "getContractById" | "getContractHashes"
138 | "hasMethod" | "isContract" | "getMinimumDeploymentFee") => true,
139 (ContractManagement, "update" | "destroy" | "setMinimumDeploymentFee") => false,
140 (Policy, "getFeePerByte" | "getExecFeeFactor" | "getExecPicoFeeFactor" | "getStoragePrice"
141 | "getMillisecondsPerBlock" | "getMaxValidUntilBlockIncrement" | "getMaxTraceableBlocks"
142 | "getAttributeFee" | "blockAccount" | "unblockAccount" | "isBlocked"
143 | "getBlockedAccounts" | "recoverFund" | "getWhitelistFeeContracts") => true,
144 (Policy, "setFeePerByte" | "setExecFeeFactor" | "setStoragePrice" | "setAttributeFee"
145 | "setMillisecondsPerBlock" | "setMaxValidUntilBlockIncrement"
146 | "setMaxTraceableBlocks" | "setWhitelistFeeContract"
147 | "removeWhitelistFeeContract") => false,
148 (Oracle, "getPrice" | "verify") => true,
149 (Oracle, "request" | "setPrice" | "finish") => false,
150 (RoleManagement, "getDesignatedByRole") => true,
151 (RoleManagement, "designateAsRole") => false,
152 (Notary, "balanceOf" | "expirationOf" | "lockDepositUntil" | "withdraw"
153 | "getMaxNotValidBeforeDelta" | "verify") => true,
154 (Notary, "setMaxNotValidBeforeDelta" | "onNEP17Payment") => false,
155 (Treasury, "verify") => true,
156 (Treasury, "onNEP17Payment" | "onNEP11Payment") => false,
157 (Ledger, "currentHash" | "getTransactionSigners" | "getTransactionVMState") => true,
158 _ => return None,
159 };
160
161 Some(has_return)
162}
163
164fn emit_syscall(bytecode: &mut Vec<u8>, name: &str) {
165 bytecode.push(0x41);
166 bytecode.extend_from_slice(&crate::codegen::interop_id_bytes(name));
167}