1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361
use concordium_rust_sdk as sdk;
use concordium_rust_sdk::{
base::{
base::{AccountAddressEq, Energy},
common::types::{CredentialIndex, KeyIndex, Signature},
constants::ED25519_SIGNATURE_LENGTH,
contracts_common::{
self, AccountAddress, AccountBalance, Address, Amount, ContractAddress, Deserial,
EntrypointName, ExchangeRate, ModuleReference, OwnedContractName, OwnedEntrypointName,
OwnedPolicy, ParseResult, SlotTime, Timestamp,
},
hashes::BlockHash,
id::types::SchemeId,
smart_contracts::{
ContractEvent, ContractTraceElement, InstanceUpdatedEvent, OwnedParameter,
OwnedReceiveName, WasmVersion,
},
transactions::AccountAccessStructure,
},
smart_contracts::engine::{
v1::{
self, trie, DebugTracker, EmittedDebugStatement, HostCall, HostFunctionV1, ReturnValue,
},
wasm::artifact,
InterpreterEnergy,
},
};
use std::{
collections::{BTreeMap, BTreeSet},
path::PathBuf,
sync::Arc,
};
use thiserror::Error;
/// A smart contract module.
#[derive(Debug, Clone)]
pub struct ContractModule {
/// Size of the module in bytes. Used for cost accounting.
pub size: u64,
/// The runnable module.
pub artifact: Arc<artifact::Artifact<v1::ProcessedImports, artifact::CompiledFunction>>,
}
/// The chain parameters.
#[derive(Debug)]
pub(crate) struct ChainParameters {
/// The block time viewable inside the smart contracts.
/// Defaults to `0`.
pub(crate) block_time: SlotTime,
/// MicroCCD per Euro ratio.
pub(crate) micro_ccd_per_euro: ExchangeRate,
/// Euro per Energy ratio.
pub(crate) euro_per_energy: ExchangeRate,
}
/// The connection and runtime needed for communicating with an external node.
#[derive(Debug)]
pub(crate) struct ExternalNodeConnection {
/// An instantiated v2 Client from the Rust SDK. Used for communicating with
/// a node.
pub(crate) client: concordium_rust_sdk::v2::Client,
/// A Tokio runtime used to execute the async methods of the `client`.
pub(crate) runtime: tokio::runtime::Runtime,
/// The block used for queries.
pub(crate) query_block: BlockHash,
/// External accounts that are verified to exist in the `query_block`.
pub(crate) accounts: BTreeSet<ExternalAccountAddress>,
/// External contracts that are verified to exist in the `query_block`.
pub(crate) contracts: BTreeSet<ExternalContractAddress>,
}
/// Represents the blockchain and supports a number of operations, including
/// creating accounts, deploying modules, initializing contract, updating
/// contracts and invoking contracts.
#[derive(Debug)]
pub struct Chain {
pub(crate) parameters: ChainParameters,
/// Accounts and info about them.
/// This uses [`AccountAddressEq`] to ensure that account aliases are seen
/// as one account.
pub accounts: BTreeMap<AccountAddressEq, Account>,
/// Smart contract modules.
pub modules: BTreeMap<ModuleReference, ContractModule>,
/// Smart contract instances.
pub contracts: BTreeMap<ContractAddress, Contract>,
/// Next contract index to use when creating a new instance.
pub(crate) next_contract_index: u64,
/// An optional connection to an external node.
pub(crate) external_node_connection: Option<ExternalNodeConnection>,
}
/// A builder for the [`Chain`].
#[derive(Debug)]
pub struct ChainBuilder {
/// The configured endpoint for an external node connection.
pub(crate) external_node_endpoint: Option<sdk::v2::Endpoint>,
/// The block hash to be used for external queries. If this is not set, then
/// the last final block hash is used instead.
pub(crate) external_query_block: Option<BlockHash>,
/// The configured exchange rate between microCCD and euro.
pub(crate) micro_ccd_per_euro: Option<ExchangeRate>,
/// Whether the microCCD/euro exchange rate should be set via the external
/// node.
pub(crate) micro_ccd_per_euro_from_external: bool,
/// The configured exchange rate between euro and energy.
pub(crate) euro_per_energy: Option<ExchangeRate>,
/// Whether the euro/energy exchange rate should be set via the external
/// node.
pub(crate) euro_per_energy_from_external: bool,
/// The configured block time.
pub(crate) block_time: Option<Timestamp>,
/// Whether the block time should be set via the external node.
pub(crate) block_time_from_external: bool,
}
/// A smart contract instance.
#[derive(Clone, Debug)]
pub struct Contract {
/// The address of this contract.
pub address: ContractAddress,
/// The module which contains this contract.
pub module_reference: ModuleReference,
/// The name of the contract.
pub contract_name: OwnedContractName,
/// The contract state.
pub state: trie::PersistentState,
/// The owner of the contract.
pub owner: AccountAddress,
/// The balance of the contract.
pub self_balance: Amount,
}
/// An account.
#[derive(Clone, Debug)]
pub struct Account {
pub address: AccountAddress,
/// The account balance.
pub balance: AccountBalance,
/// Account policy.
pub policy: OwnedPolicy,
/// Account's public keys.
pub keys: AccountAccessStructure,
}
/// A signature with account's keys.
#[derive(Debug, Clone)]
pub struct AccountSignatures {
/// It is assumed that the inner `Signature` will always be for ed25519
/// scheme, so will have length 64 bytes.
pub(crate) sigs: BTreeMap<CredentialIndex, BTreeMap<KeyIndex, Signature>>,
}
impl From<AccountSignatures> for BTreeMap<CredentialIndex, BTreeMap<KeyIndex, Signature>> {
fn from(value: AccountSignatures) -> Self { value.sigs }
}
impl From<BTreeMap<CredentialIndex, BTreeMap<KeyIndex, Signature>>> for AccountSignatures {
fn from(sigs: BTreeMap<CredentialIndex, BTreeMap<KeyIndex, Signature>>) -> Self {
Self {
sigs,
}
}
}
impl AccountSignatures {
/// Return the number of signatures contained in the structure.
pub fn num_signatures(&self) -> u32 { self.sigs.values().map(|v| v.len() as u32).sum() }
}
impl contracts_common::Serial for AccountSignatures {
fn serial<W: contracts_common::Write>(&self, out: &mut W) -> Result<(), W::Err> {
(self.sigs.len() as u8).serial(out)?;
for (k, v) in self.sigs.iter() {
k.serial(out)?;
(v.len() as u8).serial(out)?;
for (ki, sig) in v.iter() {
ki.serial(out)?;
// ed25519 scheme tag.
0u8.serial(out)?;
out.write_all(&sig.sig)?;
}
}
Ok(())
}
}
impl contracts_common::Deserial for AccountSignatures {
fn deserial<R: contracts_common::Read>(source: &mut R) -> contracts_common::ParseResult<Self> {
// We essentially unroll the definitions of `deserial_map_no_length` here since
// the inner type, the Signature, does not have exactly the right
// serialization instance that we need.
use contracts_common::Get;
let outer_len = u8::deserial(source)?;
let mut last = None;
let mut sigs = BTreeMap::new();
for _ in 0..outer_len {
let idx = source.get()?;
if last >= Some(idx) {
return Err(contracts_common::ParseError {});
}
last = Some(idx);
let inner_len: u8 = source.get()?;
let mut inner_map = BTreeMap::new();
let mut last_inner = None;
for _ in 0..inner_len {
let k = source.get()?;
let sig = match source.get()? {
SchemeId::Ed25519 => {
let mut sig = vec![0u8; ED25519_SIGNATURE_LENGTH];
source.read_exact(&mut sig)?;
Signature {
sig,
}
}
};
if let Some((old_k, old_v)) = last_inner.take() {
if k <= old_k {
return Err(contracts_common::ParseError {});
}
inner_map.insert(old_k, old_v);
}
last_inner = Some((k, sig));
}
if let Some((k, v)) = last_inner {
inner_map.insert(k, v);
}
sigs.insert(idx, inner_map);
}
Ok(Self {
sigs,
})
}
}
/// A signer with a number of keys, the amount of which affects the cost of
/// transactions.
#[derive(Copy, Clone, Debug)]
pub struct Signer {
/// The number of keys used for signing.
pub(crate) num_keys: u32,
}
/// A transfer from a contract to an account.
#[derive(Debug, PartialEq, Eq)]
pub struct Transfer {
/// The sender contract.
pub from: ContractAddress,
/// The amount transferred.
pub amount: Amount,
/// The receive account.
pub to: AccountAddress,
}
/// Represents a successful deployment of a [`ContractModule`].
#[derive(Debug, PartialEq, Eq)]
pub struct ModuleDeploySuccess {
/// The reference of the module deployed.
pub module_reference: ModuleReference,
/// The energy used for deployment.
pub energy_used: Energy,
/// Cost of transaction.
pub transaction_fee: Amount,
}
/// An error that occurred while deploying a [`ContractModule`].
#[derive(Debug, Error)]
#[error(
"Module deployment failed after consuming {energy_used}NRG ({transaction_fee} microCCD) with \
error {kind}."
)]
pub struct ModuleDeployError {
/// The energy used for deployment.
pub energy_used: Energy,
/// The transaction fee. This is the amount charged to the `sender`
/// account.
pub transaction_fee: Amount,
/// The specific reason for why the deployment failed.
pub kind: ModuleDeployErrorKind,
}
/// The specific kind of error that occurred while deploying a
/// [`ContractModule`].
#[derive(Debug, Error)]
pub enum ModuleDeployErrorKind {
/// The module provided is not valid.
#[error("Module is invalid due to: {0}")]
InvalidModule(#[from] ModuleInvalidError),
/// The sender account does not have sufficient funds to pay for the
/// deployment.
#[error("Sender does not have sufficient funds to pay for the energy")]
InsufficientFunds,
/// The sender account deploying the module does not exist.
#[error("Sender account {} does not exist", 0.0)]
SenderDoesNotExist(#[from] AccountDoesNotExist),
/// The module has already been deployed.
#[error("Module with reference {0} already exists")]
DuplicateModule(ModuleReference),
/// The module version is not supported.
#[error("Wasm version {0} is not supported")]
UnsupportedModuleVersion(WasmVersion),
}
/// An error that can occur while loading a smart contract module.
#[derive(Debug, Error)]
#[error("Could not load the module file '{path}' due to: {kind}")]
pub struct ModuleLoadError {
/// The module file.
pub path: PathBuf,
/// The reason why loading the module failed.
pub kind: ModuleLoadErrorKind,
}
/// The specific reason why loading a module failed.
#[derive(Debug, Error)]
pub enum ModuleLoadErrorKind {
/// Failed to open the module file for reading.
#[error("Could not open the file for reading to: {0}")]
OpenFile(#[from] std::io::Error),
/// Failed to read the module from the file.
#[error("Could not read the module due to: {0}")]
ReadModule(#[from] ModuleReadError),
/// The module version is not supported.
#[error("The module has wasm version {0}, which is not supported")]
UnsupportedModuleVersion(WasmVersion),
}
/// The error produced when trying to read a smart contract
/// module from a file.
#[derive(Debug, Error)]
#[error("The module could not be read due to: {0}")]
pub struct ModuleReadError(#[from] pub(crate) anyhow::Error);
/// The error produced when trying to parse a smart contract module.
#[derive(Debug, Error)]
#[error("The module is invalid to: {0}")]
pub struct ModuleInvalidError(#[from] pub(crate) anyhow::Error);
/// Represents a successful initialization of a contract.
#[derive(Debug)]
pub struct ContractInitSuccess {
/// The address of the new instance.
pub contract_address: ContractAddress,
/// Contract events (logs) produced during initialization.
pub events: Vec<ContractEvent>,
/// Energy used.
pub energy_used: Energy,
/// Cost of transaction.
pub transaction_fee: Amount,
/// Debug information emitted by the initialization method.
pub debug_trace: DebugTracker,
}
/// An error that occurred in [`Chain::contract_init`].
#[derive(Debug, Error)]
#[error(
"Contract initialization failed after consuming {energy_used}NRG ({transaction_fee} microCCD) \
with error {kind}."
)]
pub struct ContractInitError {
/// Energy used.
pub energy_used: Energy,
/// The transaction fee. This is the amount charged to the `sender`
/// account.
pub transaction_fee: Amount,
/// The specific reason for why the initialization failed.
pub kind: ContractInitErrorKind,
}
/// Types of errors that can occur in [`Chain::contract_init`].
#[derive(Debug, Error)]
pub enum ContractInitErrorKind {
/// Initialization during execution.
#[error("Failed with an execution error: {error:?}")]
ExecutionError {
/// The reason for why the contract initialization failed.
error: InitExecutionError,
/// Trace of the execution until the error.
debug_trace: DebugTracker,
},
/// Ran out of energy.
#[error("Ran out of energy: {debug_trace:?}")]
OutOfEnergy {
debug_trace: DebugTracker,
},
/// Module has not been deployed in the test environment.
#[error("{0}")]
ModuleDoesNotExist(#[from] ModuleDoesNotExist),
/// The specified contract does not exist in the module.
#[error("The contract (init name) '{name}' does not exist in the module")]
ContractNotPresentInModule {
/// The name of the contract (init method) which is not present.
name: OwnedContractName,
},
/// The sender account has not been created in test environment.
#[error("Sender missing: {0}")]
SenderDoesNotExist(#[from] AccountDoesNotExist),
/// The invoker account does not have enough funds to pay for the energy
/// reserved.
#[error("Invoker does not have enough funds to pay for the energy")]
InsufficientFunds,
/// The invoker account does not have enough funds to pay for the amount.
/// However it does it have enough funds for the energy reserved.
#[error("Invoker does not have enough funds to pay for the amount")]
AmountTooLarge,
/// The parameter is too large.
#[error("The provided parameter exceeds the maximum size allowed")]
ParameterTooLarge,
}
/// The reason for why a contract initialization failed during execution.
#[derive(Debug)]
pub enum InitExecutionError {
/// The contract rejected.
Reject {
/// The error code for why it rejected.
reason: i32,
/// The return value.
return_value: ReturnValue,
},
/// The contract trapped.
Trap {
error: ExecutionError,
},
/// The contract ran out of energy.
OutOfEnergy,
}
/// An error that occurred while executing a contract init or receive function.
#[derive(Debug, Error)]
#[error("The contract execution halted due to: {0}")]
pub struct ExecutionError(#[from] pub(crate) anyhow::Error);
/// Represents a successful contract update (or invocation).
#[derive(Debug)]
pub struct ContractInvokeSuccess {
/// Host events that occurred. This includes interrupts, resumes, and
/// upgrades.
pub trace_elements: Vec<DebugTraceElement>,
/// Energy used.
pub energy_used: Energy,
/// Cost of transaction.
pub transaction_fee: Amount,
/// The returned value.
pub return_value: ReturnValue,
/// Whether the state of the invoked contract was changed.
pub state_changed: bool,
/// The new balance of the smart contract.
pub new_balance: Amount,
}
/// Represents a successful external contract invocation.
#[derive(Debug)]
pub struct ContractInvokeExternalSuccess {
/// Host events that occurred. This includes interrupts, resumes, and
/// upgrades.
pub trace_elements: Vec<ContractTraceElement>,
/// The energy used.
pub energy_used: Energy,
/// The returned value.
pub return_value: ReturnValue,
}
/// Information about the collected debug output. This is the item returned
/// by the `debug_events` iterator. It corresponds to a section of execution
/// between interrupts.
pub struct DebugItem<'a> {
/// The address of the instance that generated the event.
pub address: ContractAddress,
/// The name of the entrypoint that generated the event.
pub entrypoint: EntrypointName<'a>,
/// The debug trace generated since the previous debug item.
pub debug_trace: &'a DebugTracker,
/// `true` if this output is in the part of execution that has been rolled
/// back.
pub rolled_back: bool,
}
/// Information about an emitted host function call. This is the item returned
/// by the `host_calls` iterator.
pub struct HostCallInfo<'a> {
/// The address of the instance that generated the event.
pub address: ContractAddress,
/// The name of the entrypoint that generated the event.
pub entrypoint: contracts_common::EntrypointName<'a>,
/// The host function that was called.
pub host_function: HostFunctionV1,
/// Energy used by the call.
pub energy_used: InterpreterEnergy,
/// `true` if this host call occurred in the part of execution that is
/// rolled back.
pub rolled_back: bool,
}
impl ContractInvokeSuccess {
/// Extract all the events logged by all the contracts in the invocation.
/// The events are returned in the order that they are emitted, and are
/// paired with the address of the contract that emitted it.
///
/// Only events from effective trace elements are included. See
/// [`Self::effective_trace_elements`] for more details.
pub fn events(&self) -> impl Iterator<Item = (ContractAddress, &[ContractEvent])> {
self.effective_trace_elements().flat_map(|cte| match cte {
ContractTraceElement::Updated {
data,
} => Some((data.address, data.events.as_slice())),
ContractTraceElement::Interrupted {
address,
events,
} => Some((*address, events.as_slice())),
_ => None,
})
}
/// Extract the transfers **to accounts** that occurred during
/// invocation. The return value is an iterator over triples `(from, amount,
/// to)` where `from` is the sender contract, and `to` is the receiver
/// account. The transfers are returned in the order that they occurred.
///
/// Only tranfers from effective trace elements are included. See
/// [`Self::effective_trace_elements`] for more details.
pub fn account_transfers(
&self,
) -> impl Iterator<Item = (ContractAddress, Amount, AccountAddress)> + '_ {
self.effective_trace_elements().flat_map(|cte| {
if let ContractTraceElement::Transferred {
from,
amount,
to,
} = cte
{
Some((*from, *amount, *to))
} else {
None
}
})
}
/// Get an iterator over references of all the [`ContractTraceElement`]s
/// that have *not* been rolled back.
///
/// The trace elements returned here corresponds to the ones returned by the
/// node.
///
/// See also [`Self::effective_trace_elements_cloned`] for a version with
/// clones.
pub fn effective_trace_elements(&self) -> impl Iterator<Item = &ContractTraceElement> {
self.trace_elements.iter().filter_map(|cte| match cte {
DebugTraceElement::Regular {
trace_element,
..
} => Some(trace_element),
DebugTraceElement::WithFailures {
..
} => None,
DebugTraceElement::Debug {
..
} => None,
})
}
/// Get an iterator over clones of all the [`ContractTraceElement`]s that
/// have *not* been rolled back.
///
/// The trace elements returned here corresponds to the ones returned by the
/// node.
///
/// See also [`Self::effective_trace_elements`] for a version with
/// references.
pub fn effective_trace_elements_cloned(&self) -> Vec<ContractTraceElement> {
self.trace_elements
.iter()
.filter_map(|cte| match cte {
DebugTraceElement::Regular {
trace_element,
..
} => Some(trace_element.clone()),
DebugTraceElement::WithFailures {
..
} => None,
DebugTraceElement::Debug {
..
} => None,
})
.collect()
}
/// Get the successful trace elements grouped by which contract they
/// originated from.
pub fn trace_elements(&self) -> BTreeMap<ContractAddress, Vec<ContractTraceElement>> {
let mut map: BTreeMap<ContractAddress, Vec<ContractTraceElement>> = BTreeMap::new();
for event in self.effective_trace_elements() {
map.entry(event.affected_address())
.and_modify(|v| v.push(event.clone()))
.or_insert_with(|| vec![event.clone()]);
}
map
}
/// Get the successful contract updates that happened in the transaction.
/// The order is the order of returns. Concretely, if A calls B (and no
/// other calls are made) then first there will be "B updated" event,
/// followed by "A updated", assuming the invocation of both succeeded.
pub fn updates(&self) -> impl Iterator<Item = &InstanceUpdatedEvent> {
self.effective_trace_elements().filter_map(|e| {
if let ContractTraceElement::Updated {
data,
} = e
{
Some(data)
} else {
None
}
})
}
/// Check whether any rollbacks occurred.
///
/// That is, whether any contract calls failed which lead to state and
/// balances being rolled back.
///
/// If `true` is returned, the relevant traces can be seen in the
/// `self.trace_elements` vector.
pub fn rollbacks_occurred(&self) -> bool {
self.trace_elements
.iter()
.any(|element| matches!(element, DebugTraceElement::WithFailures { .. }))
}
/// Try to parse the return value into a type that implements [`Deserial`].
///
/// Ensures that all bytes of the return value are read.
pub fn parse_return_value<T: Deserial>(&self) -> ParseResult<T> {
use contracts_common::{Cursor, Get, ParseError};
let mut cursor = Cursor::new(&self.return_value);
let res = cursor.get()?;
// Check that all bytes have been read, as leftover bytes usually indicate
// errors.
if cursor.offset != self.return_value.len() {
return Err(ParseError::default());
}
Ok(res)
}
}
/// The different types of debug output that can be printed by the
/// [`print_debug`](DebugInfoExt::print_debug) method.
pub enum DebugOutputKind {
/// Output everything, all host calls and emitted events.
Full,
/// Only output emitted events.
EmittedEvents,
/// Output all host calls in the order they were emitted.
HostCalls,
/// Output host call summary grouped per instance that was affected.
HostCallsSummary,
/// Output host call summary grouped per instance that was affected and
/// entrypoint.
HostCallsSummaryPerEntrypoint,
}
/// A trait implemented by types which can extract debug information from
/// contract receive entrypoint executions.
//
// Note for maintainers. The return types of many methods here are Box<dyn
// Iterator ...> where they would ideally be `impl Iterator` to both be simpler,
// and to avoid a needless memory allocation. The reason for this is that `impl
// Trait` is only supported in trait methods since Rust 1.74 and at the time of
// writing that is the latest version, and we wish to support older versions for
// the time being.
pub trait DebugInfoExt: Sized {
/// Print the desired level of debug information that was recorded.
/// This function is meant to be used in a method-chaining style.
fn print_debug(self, level: DebugOutputKind) -> Self {
match level {
DebugOutputKind::Full => {
for DebugItem {
address,
entrypoint,
debug_trace,
rolled_back,
} in self.debug_events()
{
eprintln!(
"{entrypoint} of instance at {address}{}",
if rolled_back {
" (rolled back)"
} else {
""
}
);
eprintln!("{debug_trace}");
}
}
DebugOutputKind::EmittedEvents => {
for DebugItem {
address,
entrypoint,
debug_trace,
rolled_back,
} in self.debug_events()
{
eprintln!(
"{entrypoint} of instance at {address}{}",
if rolled_back {
" (rolled back)"
} else {
""
}
);
for (_, event) in debug_trace.emitted_events.iter() {
eprintln!("{event}");
}
}
}
DebugOutputKind::HostCalls => {
for (addr, emitted_event) in self.emitted_debug_prints() {
eprintln!("{addr}:{emitted_event}");
}
}
DebugOutputKind::HostCallsSummary => {
for (addr, addr_summary) in self.host_calls_summary() {
eprintln!("Instance at {addr} host call summary.");
for (host_fn, (times, total_nrg)) in addr_summary {
eprintln!(
"- {host_fn} called {times} times totalling {total_nrg} interpreter \
energy spent."
)
}
}
}
DebugOutputKind::HostCallsSummaryPerEntrypoint => {
for ((addr, ep), addr_summary) in self.host_calls_summary_per_entrypoint() {
eprintln!("Entrypoint {ep} of instance at {addr} host call summary.");
for (host_fn, (times, total_nrg)) in addr_summary {
eprintln!(
"- {host_fn} called {times} times totalling {total_nrg} interpreter \
energy spent."
)
}
}
}
}
self
}
/// Print (to stderr) all the events generated by `concordium_dbg!`
/// statements.
fn print_emitted_events(self) -> Self { self.print_debug(DebugOutputKind::EmittedEvents) }
/// Get an iterator over all the debug traces emitted by the execution.
fn debug_events(&self) -> Box<dyn Iterator<Item = DebugItem<'_>> + '_>;
/// Get an iterator over all host calls that have occurred, both in the
/// remaining trace and in the rolled back part.
fn host_calls(&self) -> Box<dyn Iterator<Item = HostCallInfo<'_>> + '_> {
Box::new(self.debug_events().flat_map(|de| {
de.debug_trace.host_call_trace.iter().map(
move |(
_,
HostCall {
host_function,
energy_used,
},
)| {
HostCallInfo {
address: de.address,
entrypoint: de.entrypoint,
host_function: *host_function,
energy_used: *energy_used,
rolled_back: de.rolled_back,
}
},
)
}))
}
/// Get an iterator over all the emitted `concordium_dbg!` events.
fn emitted_debug_prints(
&self,
) -> Box<dyn Iterator<Item = (ContractAddress, &EmittedDebugStatement)> + '_> {
Box::new(self.debug_events().flat_map(|de| {
de.debug_trace.emitted_events.iter().map(move |(_, statement)| (de.address, statement))
}))
}
/// Get host function calls grouped by contract address that
/// generated them. The value at each address and host function is the
/// pair of the number of times the host function was called, and the total
/// amount of interpreter energy that was used by all the calls.
fn host_calls_summary(
&self,
) -> BTreeMap<ContractAddress, BTreeMap<HostFunctionV1, (usize, InterpreterEnergy)>> {
let mut out = BTreeMap::new();
for HostCallInfo {
address,
host_function,
energy_used,
..
} in self.host_calls()
{
let at_addr: &mut BTreeMap<HostFunctionV1, (usize, InterpreterEnergy)> =
out.entry(address).or_default();
let entry = at_addr.entry(host_function).or_insert((0, InterpreterEnergy::new(0)));
entry.1.energy += energy_used.energy;
entry.0 += 1;
}
out
}
/// Get host function calls grouped by contract address and entrypoint that
/// generated them. The value at each address and host function is the
/// pair of the number of times the host function was called, and the total
/// amount of interpreter energy that was used by all the calls.
fn host_calls_summary_per_entrypoint(
&self,
) -> BTreeMap<
(ContractAddress, EntrypointName<'_>),
BTreeMap<HostFunctionV1, (usize, InterpreterEnergy)>,
> {
let mut out = BTreeMap::new();
for HostCallInfo {
address,
host_function,
energy_used,
entrypoint,
..
} in self.host_calls()
{
let at_addr: &mut BTreeMap<HostFunctionV1, (usize, InterpreterEnergy)> =
out.entry((address, entrypoint)).or_default();
let entry = at_addr.entry(host_function).or_insert((0, InterpreterEnergy::new(0)));
entry.1.energy += energy_used.energy;
entry.0 += 1;
}
out
}
}
impl DebugInfoExt for ContractInvokeSuccess {
fn debug_events(&self) -> Box<dyn Iterator<Item = DebugItem<'_>> + '_> {
Box::new(debug_events_worker(false, &self.trace_elements))
}
}
impl DebugInfoExt for ContractInvokeError {
fn debug_events(&self) -> Box<dyn Iterator<Item = DebugItem<'_>> + '_> {
Box::new(debug_events_worker(true, &self.trace_elements))
}
}
impl DebugInfoExt for Result<ContractInvokeSuccess, ContractInvokeError> {
fn debug_events(&self) -> Box<dyn Iterator<Item = DebugItem<'_>> + '_> {
match self {
Ok(v) => v.debug_events(),
Err(v) => v.debug_events(),
}
}
}
/// Get an iterator over all the debug traces emitted by the execution.
fn debug_events_worker(
rolled_back: bool,
initial_events: &[DebugTraceElement],
) -> impl Iterator<Item = DebugItem<'_>> {
enum Next<'a> {
Remaining((bool, &'a [DebugTraceElement])),
Emit(DebugItem<'a>),
}
struct DebugTraceElementsIter<'a> {
stack: Vec<Next<'a>>,
}
impl<'a> Iterator for DebugTraceElementsIter<'a> {
type Item = DebugItem<'a>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let top = self.stack.pop()?;
let top = match top {
Next::Remaining(top) => top,
Next::Emit(v) => return Some(v),
};
let (first, rest) = top.1.split_first()?;
if !rest.is_empty() {
self.stack.push(Next::Remaining((top.0, rest)));
}
match first {
DebugTraceElement::Regular {
entrypoint,
trace_element,
energy_used: _,
debug_trace,
} => {
return Some(DebugItem {
address: trace_element.affected_address(),
entrypoint: entrypoint.as_entrypoint_name(),
debug_trace,
rolled_back: false,
});
}
DebugTraceElement::WithFailures {
contract_address,
entrypoint,
error: _,
trace_elements,
energy_used: _,
debug_trace,
} => {
self.stack.push(Next::Emit(DebugItem {
address: *contract_address,
entrypoint: entrypoint.as_entrypoint_name(),
debug_trace,
rolled_back: true,
}));
if !trace_elements.is_empty() {
self.stack.push(Next::Remaining((true, trace_elements)));
}
}
DebugTraceElement::Debug {
entrypoint,
address,
debug_trace,
} => {
return Some(DebugItem {
address: *address,
entrypoint: entrypoint.as_entrypoint_name(),
debug_trace,
rolled_back: false,
});
}
}
}
}
}
DebugTraceElementsIter {
stack: vec![Next::Remaining((rolled_back, initial_events))],
}
}
/// A wrapper for [`ContractTraceElement`], which provides additional
/// information for testing and debugging. Most notably, it contains trace
/// elements for failures, which are normally discarded by the node.
#[derive(Debug)]
pub enum DebugTraceElement {
/// A regular trace element with some additional data, e.g., energy usage
/// and the entrypoint.
/// This variant may be included in the `WithFailures` list of
/// trace elements.
Regular {
/// The entrypoint.
entrypoint: OwnedEntrypointName,
/// The trace element.
trace_element: ContractTraceElement,
/// The energy used so far.
energy_used: Energy,
debug_trace: DebugTracker,
},
/// A record of debug events emitted before the interrupt.
/// Contract queries, such as querying account balance or exchange rates do
/// not produce any events visible in the
/// [`Regular`](DebugTraceElement::Regular) trace elements
/// or in the transaction outcomes, but the internal debug information is
/// still useful for debugging.
Debug {
/// The entrypoint that is being executed.
entrypoint: OwnedEntrypointName,
/// The address that is affected.
address: ContractAddress,
/// Events emitted until the interrupt.
debug_trace: DebugTracker,
},
/// One or multiple trace elements that fail. Useful for debugging.
/// This variant also contains additional information, such as the error,
/// entrypoint, and energy usage.
WithFailures {
/// The address of the contract which failed.
/// This will always match the address in the last element in
/// `trace_elements` if the vector isn't empty.
contract_address: ContractAddress,
/// The entrypoint which failed.
/// This will always match the address in the last element in
/// `trace_elements` if the vector isn't empty.
entrypoint: OwnedEntrypointName,
/// The error returned.
error: InvokeExecutionError,
/// Intermediate [`DebugTraceElement`]s which occurred prior to failing.
/// These are the elements which are normally discared by the node.
trace_elements: Vec<DebugTraceElement>,
/// The energy used so far.
energy_used: Energy,
/// Detailed breakdown of debug output and host calls produced so far.
debug_trace: DebugTracker,
},
}
/// The reason for why a contract invocation failed during execution.
#[derive(Debug)]
pub enum InvokeExecutionError {
/// The contract rejected.
Reject {
/// The error code for why it rejected.
reason: i32,
/// The return value.
return_value: ReturnValue,
},
/// The contract trapped.
Trap {
error: ExecutionError,
},
}
/// An error that occurred during a [`Chain::contract_update`] or
/// [`Chain::contract_invoke`].
#[derive(Debug, Error)]
#[error(
"Contract invocation failed after using {energy_used}NRG ({transaction_fee} microCCD) with \
error {kind}."
)]
pub struct ContractInvokeError {
/// The energy used.
pub energy_used: Energy,
/// The transaction fee. For [`Chain::contract_update`], this is the amount
/// charged to the `invoker` account.
pub transaction_fee: Amount,
/// Trace elements that occurred before the contract failed.
pub trace_elements: Vec<DebugTraceElement>,
/// The specific reason for why the invocation failed.
pub kind: ContractInvokeErrorKind,
}
/// The error kinds that can occur during [`Chain::contract_update`] or
/// [`Chain::contract_invoke`].
#[derive(Debug, Error)]
pub enum ContractInvokeErrorKind {
/// Invocation failed during execution.
#[error("Failed during execution: {failure_kind:?}")]
ExecutionError {
failure_kind: v1::InvokeFailure,
},
/// Ran out of energy.
#[error("Ran out of energy")]
OutOfEnergy {
debug_trace: DebugTracker,
},
/// The balance of an account or contract overflowed.
/// If you are seeing this error, lower the [`Amount`]s used in your tests.
#[error("The balance of an account or contract overflowed")]
BalanceOverflow,
/// Module has not been deployed in test environment.
#[error("{0}")]
ModuleDoesNotExist(#[from] ModuleDoesNotExist),
/// Contract instance has not been initialized in the test environment.
#[error("{0}")]
ContractDoesNotExist(#[from] ContractDoesNotExist),
/// Entrypoint does not exist and neither does the fallback entrypoint.
#[error("{0}")]
EntrypointDoesNotExist(#[from] EntrypointDoesNotExist),
/// The invoker account has not been created in the test environment.
#[error("Invoker missing: {0}")]
InvokerDoesNotExist(#[from] AccountDoesNotExist),
/// The sender does not exist in the test environment.
#[error("Sender missing: the object with address '{0}' does not exist")]
SenderDoesNotExist(Address),
/// The invoker account does not have enough funds to pay for the energy
/// reserved.
#[error("Invoker does not have enough funds to pay for the energy")]
InsufficientFunds,
/// The invoker account does not have enough funds to pay for the amount.
/// However it does it have enough funds for the energy reserved.
#[error("Invoker does not have enough funds to pay for the amount")]
AmountTooLarge,
/// The parameter is too large.
#[error("The provided parameter exceeds the maximum size allowed")]
ParameterTooLarge,
}
/// The error returned when external contract invocations fail.
#[derive(Debug, Error)]
pub enum ContractInvokeExternalError {
/// The external contract invocation was executed, but resulted in a
/// failure.
#[error(
"External contract invocation was executed, but failed after using {energy_used}NRG with \
error {reason:?}."
)]
Failure {
/// The reason why the invoke failed.
reason: sdk::types::RejectReason,
/// The energy used before failure.
energy_used: Energy,
/// The value returned.
return_value: ReturnValue,
},
/// The external contract invocation failed due to an external node error.
#[error("External contract invocation failed due to an external node error: {error}")]
ExternalNodeError {
#[from]
/// The external node error.
error: ExternalNodeError,
},
}
/// A balance error which can occur when transferring [`Amount`]s.
#[derive(Debug, PartialEq, Eq, Error)]
pub(crate) enum BalanceError {
/// The sender had insufficient balance.
#[error("The sender had insufficient balance.")]
Insufficient,
/// The balance change resulted in an overflow.
///
/// This is a configuration error in the tests, where unrealistic balances
/// have been set, and should thus be *unrecoverable*.
///
/// On the chain there is roughly 10 billion CCD, so an overflow wil never
/// occur when adding CCDs.
#[error("An overflow on CCD amounts occurred.")]
Overflow,
}
/// Errors related to transfers.
#[derive(Debug, PartialEq, Eq, Error)]
pub(crate) enum TransferError {
/// The receiver does not exist.
#[error("The receiver does not exist.")]
ToMissing,
/// A balance error occurred.
#[error("A balance error occurred: {error:?}")]
BalanceError {
#[from]
error: BalanceError,
},
}
/// The entrypoint does not exist.
#[derive(PartialEq, Eq, Debug, Error)]
#[error("Entrypoint '{entrypoint}' does not exist.")]
pub struct EntrypointDoesNotExist {
/// The missing entrypoint.
pub entrypoint: OwnedEntrypointName,
}
/// The contract module does not exist.
#[derive(Debug, Error)]
#[error("Module '{module_reference}' does not exist.")]
pub struct ModuleDoesNotExist {
/// The reference of the missing module.
pub module_reference: ModuleReference,
}
/// The contract instance does not exist.
#[derive(Debug, Error)]
#[error("Contract instance '{address}' does not exist.")]
pub struct ContractDoesNotExist {
/// The address of the missing contract.
pub address: ContractAddress,
}
/// The account does not exist.
#[derive(Debug, Error)]
#[error("Account '{address}' does not exist.")]
pub struct AccountDoesNotExist {
/// The address of the missing account.
pub address: AccountAddress,
}
/// The provided exchange rates are not valid.
/// Meaning that they do not correspond to one energy costing less than
/// `u64::MAX / 100_000_000_000`.
#[derive(Debug, Error)]
#[error("An exchange rate was too high.")]
pub struct ExchangeRateError;
/// A [`Signer`] cannot be created with `0` keys.
#[derive(Debug, Error)]
#[error("Any signer must have at least one key.")]
pub struct ZeroKeysError;
/// Errors that occur while setting up the connection to an external node.
#[derive(Debug, thiserror::Error)]
pub enum SetupExternalNodeError {
/// It was not possible to connect to a node on the provided endpoint.
#[error("Could not connect to the provided endpoint due to: {error}")]
CannotConnect {
/// The inner error.
#[from]
error: sdk::endpoints::Error,
},
/// The attempt to connect to an external node timed out.
#[error("The attempt to connect to an external node timed out.")]
ConnectTimeout,
/// The query to check the `external_query_block` timed out.
#[error("The query to check the `external_query_block` timed out.")]
CheckQueryBlockTimeout,
/// The specified external query block does not exist.
#[error("The specified external query block {query_block} does not exist.")]
QueryBlockDoesNotExist {
query_block: BlockHash,
},
/// Could not check the existence of the specified query block or the last
/// final block.
#[error(
"Could not check the existence of the specified query block or the last final block due \
to: {error}"
)]
CannotCheckQueryBlockExistence {
/// The inner error.
error: sdk::v2::RPCError,
},
}
/// Errors that occur while trying to communicate with an external node.
#[derive(Debug, Error)]
pub enum ExternalNodeError {
/// An external node has not been configured.
#[error("An external node has not been configured.")]
NotConfigured,
/// The query could not be performed.
#[error("Could not perform the query: {error}")]
QueryError {
#[from]
error: sdk::endpoints::QueryError,
},
/// The query timed out.
#[error("The query timed out.")]
QueryTimeout,
}
/// The error returned when an external node has not been configured prior to
/// using it.
#[derive(Debug, Error, PartialEq, Eq)]
#[error("An external node has not been configured.")]
pub struct ExternalNodeNotConfigured;
#[derive(Debug, Error)]
pub enum ChainBuilderError {
/// The provided exchange rates are not valid.
/// Meaning that they do not correspond to one energy costing less than
/// `u64::MAX / 100_000_000_000`.
#[error("An exchange rate was too high.")]
ExchangeRateError,
/// An error occurred while setting up the connection to an external node.
#[error("Error occurred while setting up the connection to an external node: {error}")]
SetupExternalNodeError {
#[from]
error: SetupExternalNodeError,
},
/// Error occurred while using the external node for querying chain
/// parameters such as the block time or exchange rates.
#[error("Error occurred while using the external node for querying chain parameters: {error}")]
ExternalNodeError {
#[from]
error: ExternalNodeError,
},
/// Could not configure the block time because both the
/// [`ChainBuilder::block_time`] and
/// [`ChainBuilder::block_time_from_external`] were provided, which is not
/// allowed.
#[error(
"Conflicting block time configuration: `block_time` and `block_time_from_external` cannot \
both be used."
)]
ConflictingBlockTime,
/// Could not configure the microCCD/euro exchange rate because both the
/// [`ChainBuilder::micro_ccd_per_euro`] and
/// [`ChainBuilder::micro_ccd_per_euro_from_external`] were provided, which
/// is not allowed.
#[error(
"Conflicting microCCD per euro configuration: `micro_ccd_per_euro` and \
`micro_ccd_per_euro_from_external` cannot both be used."
)]
ConflictingMicroCCDPerEuro,
/// Could not configure the euro/energy exchange rate because both the
/// [`ChainBuilder::euro_per_energy`] and
/// [`ChainBuilder::euro_per_energy_from_external`] were provided, which is
/// not allowed.
#[error(
"Conflicting euro per energy configuration: `euro_per_energy` and \
`euro_per_energy_from_external` cannot both be used."
)]
ConflictingEuroPerEnergy,
/// A configuration option that requires an external node connection was
/// used without [`ChainBuilder::external_node_connection`].
#[error(
"A configuration method that requires an external node connection was called without \
`ChainBuilder::external_node_connection`."
)]
MissingExternalConnection,
}
/// The block time overflowed during a call to `Chain::tick_block_time`.
#[derive(Debug, Error, PartialEq, Eq)]
#[error("The block time overflowed during a call to `Chain::tick_block_time`.")]
pub struct BlockTimeOverflow;
/// The contract address of an contract on an external node.
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct ExternalContractAddress {
/// The contract address.
pub(crate) address: ContractAddress,
}
/// The address of an account on an external node.
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct ExternalAccountAddress {
/// The account address.
pub(crate) address: AccountAddress,
}
/// Either an external contract address or an external account address.
///
/// External means that it is an entity that exists on an external node.
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum ExternalAddress {
Account(ExternalAccountAddress),
Contract(ExternalContractAddress),
}
impl ExternalAddress {
/// Convert to an [`Address`].
///
/// This is an internal method instead of a [`From`] implementation, as it
/// should be difficult to conflate external and regular addresses.
pub(crate) fn to_address(self) -> Address {
match self {
ExternalAddress::Account(ExternalAccountAddress {
address,
}) => Address::Account(address),
ExternalAddress::Contract(ExternalContractAddress {
address,
}) => Address::Contract(address),
}
}
}
/// Data needed to invoke an external smart contract instance.
///
/// This is nearly identical to
/// [`UpdateContractPayload`](concordium_rust_sdk::base::transactions::UpdateContractPayload)
/// except that it uses an [`ExternalContractAddress`] instead of an
/// [`ContractAddress`].
#[derive(Debug, Clone)]
pub struct InvokeExternalContractPayload {
/// Send the given amount of CCD together with the message to the
/// contract instance.
pub amount: Amount,
/// Address of the external contract instance to invoke.
pub address: ExternalContractAddress,
/// Name of the method to invoke on the contract.
pub receive_name: OwnedReceiveName,
/// Message to send to the contract instance.
pub message: OwnedParameter,
}
impl From<ExternalAccountAddress> for ExternalAddress {
fn from(addr: ExternalAccountAddress) -> Self { Self::Account(addr) }
}
impl From<ExternalContractAddress> for ExternalAddress {
fn from(addr: ExternalContractAddress) -> Self { Self::Contract(addr) }
}