pub async fn notify(
    block_height: BlockHeight,
    max_fee: ICPTs,
    from_subaccount: Option<Subaccount>,
    to_canister: CanisterId,
    to_subaccount: Option<Subaccount>,
    notify_using_protobuf: bool,
) -> Result<BytesS, String> {
    let caller_principal_id = caller();

    if !LEDGER.read().unwrap().can_send(&caller_principal_id) {
        panic!(
            "Notifying from non-self-authenticating principal or non-whitelisted canister is not allowed: {}",
            caller_principal_id
        );
    }

    let expected_from = AccountIdentifier::new(caller_principal_id, from_subaccount);

    let expected_to = AccountIdentifier::new(to_canister.get(), to_subaccount);

    if max_fee != TRANSACTION_FEE {
        panic!("Transaction fee should be {}", TRANSACTION_FEE);
    }

    // This transaction provides and on chain record that a notification was
    // attempted
    let transfer = Transfer::Send {
        from: expected_from,
        to: expected_to,
        amount: ICPTs::ZERO,
        fee: max_fee,
    };

    // While this payment has been made here, it isn't actually committed until you
    // make an inter canister call. As such we don't reject without rollback until
    // an inter-canister call has definitely been made
    add_payment(Memo(block_height), transfer, None);

    let raw_block: EncodedBlock =
        match block(block_height).unwrap_or_else(|| panic!("Block {} not found", block_height)) {
            Ok(raw_block) => raw_block,
            Err(cid) => {
                print(format!(
                    "Searching canister {} for block {}",
                    cid, block_height
                ));
                // Lookup the block on the archive
                let BlockRes(res) = call_with_cleanup(cid, "get_block_pb", protobuf, block_height)
                    .await
                    .map_err(|e| format!("Failed to fetch block {}", e.1))?;
                res.ok_or("Block not found")?
                    .map_err(|c| format!("Tried to redirect lookup a second time to {}", c))?
            }
        };

    let block = raw_block.decode().unwrap();

    let (from, to, amount) = match block.transaction().transfer {
        Transfer::Send {
            from, to, amount, ..
        } => (from, to, amount),
        _ => panic!("Notification failed transfer must be of type send"),
    };

    assert_eq!(
        (from, to),
        (expected_from, expected_to),
        "sender and recipient must match the specified block"
    );

    let transaction_notification_args = TransactionNotification {
        from: caller_principal_id,
        from_subaccount,
        to: to_canister,
        to_subaccount,
        block_height,
        amount,
        memo: block.transaction().memo,
    };

    let block_timestamp = block.timestamp();

    change_notification_state(block_height, block_timestamp, true).expect("Notification failed");

    let response = if notify_using_protobuf {
        let bytes = ProtoBuf(transaction_notification_args)
            .into_bytes()
            .expect("transaction notification serialization failed");
        call_bytes_with_cleanup(
            to_canister,
            "transaction_notification_pb",
            &bytes[..],
            Funds::zero(),
        )
        .await
    } else {
        let bytes = candid::encode_one(transaction_notification_args)
            .expect("transaction notification serialization failed");
        call_bytes_with_cleanup(
            to_canister,
            "transaction_notification",
            &bytes[..],
            Funds::zero(),
        )
        .await
    };
    // Don't panic after here or the notification may be locked like it succeeded
    // when actually it failed

    // propagate the response/rejection from 'to_canister' if it's shorter than this
    // length.
    // This could be done better because we still read in the whole
    // String/Vec as is
    pub const MAX_LENGTH: usize = 8192;

    match response {
        Ok(bs) => {
            if bs.len() > MAX_LENGTH {
                let caller = caller();
                Err(format!(
                    "Notification succeeded, but the canister '{}' returned too large of a response",
                    caller,
                ))
            } else {
                Ok(BytesS(bs))
            }
        }
        Err((_code, err)) => {
            // It may be that by the time this callback is made the block will have been
            // garbage collected. That is fine because we don't inspect the
            // response here.
            let _ = change_notification_state(block_height, block_timestamp, false);
            if err.len() > MAX_LENGTH {
                let caller = caller();
                Err(format!(
                    "Notification failed, but the canister '{}' returned too large of a response",
                    caller,
                ))
            } else {
                Err(format!("Notification failed with message '{}'", err))
            }
        }
    }
}
/// You can notify a canister that you have made a payment to it. The
/// payment must have been made to the account of a canister and from the
/// callers account. You cannot notify a canister about a transaction it has
/// already been successfully notified of. If the canister rejects the
/// notification call it is not considered to have been notified.
///
/// # Arguments
///
/// * `block_height` -  The height of the block you would like to send a
///   notification about
/// * `to_canister` - The canister that received the payment
/// * `to_subaccount` - The subaccount that received the payment
You can notify a canister that you have made a payment to it. The
payment must have been made to the account of a canister and from the
callers account. You cannot notify a canister about a transaction it has
already been successfully notified of. If the canister rejects the
notification call it is not considered to have been notified.
You can notify a canister that you have made a payment to it . The payment must have been made to the account of a canister and from the callers account . You cannot notify a canister about a transaction it has already been successfully notified of . If the canister rejects the notification call it is not considered to have been notified .
pub async fn notify(
    block_height: BlockHeight,
    max_fee: ICPTs,
    from_subaccount: Option<Subaccount>,
    to_canister: CanisterId,
    to_subaccount: Option<Subaccount>,
    notify_using_protobuf: bool,
) -> Result<BytesS, String> {
    let caller_principal_id = caller();
    if !LEDGER.read().unwrap().can_send(&caller_principal_id) {
        panic!(
            "Notifying from non-self-authenticating principal or non-whitelisted canister is not allowed: {}",
            caller_principal_id
        );
    }
    let expected_from = AccountIdentifier::new(caller_principal_id, from_subaccount);
    let expected_to = AccountIdentifier::new(to_canister.get(), to_subaccount);
    if max_fee != TRANSACTION_FEE {
        panic!("Transaction fee should be {}", TRANSACTION_FEE);
    }
    // This transaction provides and on chain record that a notification was
    // attempted
    let transfer = Transfer::Send {
        from: expected_from,
        to: expected_to,
        amount: ICPTs::ZERO,
        fee: max_fee,
    };
    // While this payment has been made here, it isn't actually committed until you
    // make an inter canister call. As such we don't reject without rollback until
    // an inter-canister call has definitely been made
    add_payment(Memo(block_height), transfer, None);
    let raw_block: EncodedBlock =
        match block(block_height).unwrap_or_else(|| panic!("Block {} not found", block_height)) {
            Ok(raw_block) => raw_block,
            Err(cid) => {
                print(format!(
                    "Searching canister {} for block {}",
                    cid, block_height
                ));
                // Lookup the block on the archive
                let BlockRes(res) = call_with_cleanup(cid, "get_block_pb", protobuf, block_height)
                    .await
                    .map_err(|e| format!("Failed to fetch block {}", e.1))?;
                res.ok_or("Block not found")?
                    .map_err(|c| format!("Tried to redirect lookup a second time to {}", c))?
            }
        };
    let block = raw_block.decode().unwrap();
    let (from, to, amount) = match block.transaction().transfer {
        Transfer::Send {
            from, to, amount, ..
        } => (from, to, amount),
        _ => panic!("Notification failed transfer must be of type send"),
    };
    assert_eq!(
        (from, to),
        (expected_from, expected_to),
        "sender and recipient must match the specified block"
    );
    let transaction_notification_args = TransactionNotification {
        from: caller_principal_id,
        from_subaccount,
        to: to_canister,
        to_subaccount,
        block_height,
        amount,
        memo: block.transaction().memo,
    };
    let block_timestamp = block.timestamp();
    change_notification_state(block_height, block_timestamp, true).expect("Notification failed");
    let response = if notify_using_protobuf {
        let bytes = ProtoBuf(transaction_notification_args)
            .into_bytes()
            .expect("transaction notification serialization failed");
        call_bytes_with_cleanup(
            to_canister,
            "transaction_notification_pb",
            &bytes[..],
            Funds::zero(),
        )
        .await
    } else {
        let bytes = candid::encode_one(transaction_notification_args)
            .expect("transaction notification serialization failed");
        call_bytes_with_cleanup(
            to_canister,
            "transaction_notification",
            &bytes[..],
            Funds::zero(),
        )
        .await
    };
    // Don't panic after here or the notification may be locked like it succeeded
    // when actually it failed
    // propagate the response/rejection from 'to_canister' if it's shorter than this
    // length.
    // This could be done better because we still read in the whole
    // String/Vec as is
    pub const MAX_LENGTH: usize = 8192;
    match response {
        Ok(bs) => {
            if bs.len() > MAX_LENGTH {
                let caller = caller();
                Err(format!(
                    "Notification succeeded, but the canister '{}' returned too large of a response",
                    caller,
                ))
            } else {
                Ok(BytesS(bs))
            }
        }
        Err((_code, err)) => {
            // It may be that by the time this callback is made the block will have been
            // garbage collected. That is fine because we don't inspect the
            // response here.
            let _ = change_notification_state(block_height, block_timestamp, false);
            if err.len() > MAX_LENGTH {
                let caller = caller();
                Err(format!(
                    "Notification failed, but the canister '{}' returned too large of a response",
                    caller,
                ))
            } else {
                Err(format!("Notification failed with message '{}'", err))
            }
        }
    }
}
pub async fn notify ( block_height : BlockHeight , max_fee : ICPTs , from_subaccount : Option < Subaccount > , to_canister : CanisterId , to_subaccount : Option < Subaccount > , notify_using_protobuf : bool , ) -> Result < BytesS , String > { let caller_principal_id = caller ( ) ; if ! LEDGER . read ( ) . unwrap ( ) . can_send ( & caller_principal_id ) { panic ! ( " " caller_principal_id ) ; } let expected_from = AccountIdentifier :: new ( caller_principal_id , from_subaccount ) ; let expected_to = AccountIdentifier :: new ( to_canister . get ( ) , to_subaccount ) ; if max_fee != TRANSACTION_FEE { panic ! ( " " TRANSACTION_FEE ) ; } // This transaction provides and on chain record that a notification was // attempted let transfer = Transfer :: Send { from : expected_from , to : expected_to , amount : ICPTs :: ZERO , fee : max_fee , } ; // While this payment has been made here, it isn't actually committed until you // make an inter canister call. As such we don't reject without rollback until // an inter-canister call has definitely been made add_payment ( Memo ( block_height ) , transfer , None ) ; let raw_block : EncodedBlock = match block ( block_height ) . unwrap_or_else ( | | panic ! ( " " block_height ) ) { Ok ( raw_block ) => raw_block , Err ( cid ) => { print ( format ! ( " " cid block_height ) ) ; // Lookup the block on the archive let BlockRes ( res ) = call_with_cleanup ( cid , " " , protobuf , block_height ) . await . map_err ( | e | format ! ( " " e 1 ) ) ? ; res . ok_or ( " " ) ? . map_err ( | c | format ! ( " " c ) ) ? } } ; let block = raw_block . decode ( ) . unwrap ( ) ; let ( from , to , amount ) = match block . transaction ( ) . transfer { Transfer :: Send { from , to , amount , .. } => ( from , to , amount ) , _ => panic ! ( " " ) , } ; assert_eq ! ( ( from to ) ( expected_from expected_to ) " " ) ; let transaction_notification_args = TransactionNotification { from : caller_principal_id , from_subaccount , to : to_canister , to_subaccount , block_height , amount , memo : block . transaction ( ) . memo , } ; let block_timestamp = block . timestamp ( ) ; change_notification_state ( block_height , block_timestamp , true ) . expect ( " " ) ; let response = if notify_using_protobuf { let bytes = ProtoBuf ( transaction_notification_args ) . into_bytes ( ) . expect ( " " ) ; call_bytes_with_cleanup ( to_canister , " " , & bytes [ .. ] , Funds :: zero ( ) , ) . await } else { let bytes = candid :: encode_one ( transaction_notification_args ) . expect ( " " ) ; call_bytes_with_cleanup ( to_canister , " " , & bytes [ .. ] , Funds :: zero ( ) , ) . await } ; // Don't panic after here or the notification may be locked like it succeeded // when actually it failed // propagate the response/rejection from 'to_canister' if it's shorter than this // length. // This could be done better because we still read in the whole // String/Vec as is pub const MAX_LENGTH : usize = 8192 ; match response { Ok ( bs ) => { if bs . len ( ) > MAX_LENGTH { let caller = caller ( ) ; Err ( format ! ( " " caller ) ) } else { Ok ( BytesS ( bs ) ) } } Err ( ( _code , err ) ) => { // It may be that by the time this callback is made the block will have been // garbage collected. That is fine because we don't inspect the // response here. let _ = change_notification_state ( block_height , block_timestamp , false ) ; if err . len ( ) > MAX_LENGTH { let caller = caller ( ) ; Err ( format ! ( " " caller ) ) } else { Err ( format ! ( " " err ) ) } } } }
You can notify a canister that you have made a payment to it.
You can notify a canister that you have made a payment to it .
block_height
BlockHeight
max_fee
ICPTs
from_subaccount
Option<Subaccount>
to_canister
CanisterId
to_subaccount
Option<Subaccount>
notify_using_protobuf
bool
block_height
BlockHeight
max_fee
ICPTs
from_subaccount
Option<Subaccount>
to_canister
CanisterId
to_subaccount
Option<Subaccount>
notify_using_protobuf
bool