Skip to the content.

Hardening interface objects

Intent

Entities can only interact with the hardened object by using the methods that the hardened object provides [1].

Consequences

Context

Agoric describes a hardened object is an object of which the properties can’t be changed [2]. To harden an object, the harden function is used. Object o can be hardened using the harden function by calling harden(o). The harden function recursively calls JavaScript’s Object.freeze function on the properties of the object that is hardened [3]. Agoric suggests that the harden function should be called on all objects that will be transferred across a trust boundary [1] [4]. The strictest way an entity could define a trust boundary, is by assuming itself as trusted, and all other entities as untrusted.

Example

//Alice creates an object which holds her contact information
const aliceContactObject = {
    name:"Alice",
    walletAddress:"12345-wallet-address-Alice-12345"
};

//Alice sends this object to Bob: the object passes the trust boundary

//Bob replaces Alice's address with his own address
aliceContactObject.walletAddress = "54321-wallet-address-Bob-54321";

//The walletAddress of aliceContactObject is no longer Alice's wallet address
t.deepEqual(aliceContactObject.walletAddress,"54321-wallet-address-Bob-54321");

The code above shows an example of what happens when harden is not called on an object that passes the trust boundary. Alice creates an object holding her contact information. She passes this object to Bob without using the harden function first. Bob is now able to change the walletAddressAlice property of the aliceContactObject object to his own wallet address. This means that, at this point in time, there is an object circulating which Alice expects to hold her wallet address, while it actually holds Bob’s address.

//Alice creates a hardened object which holds her contact information
const aliceContactObject = harden({
    name:"Alice",
    walletAddress:"12345-wallet-address-Alice-12345"
});

//Alice sends this object to Bob: the object passes the trust boundary

//If Bob tries to replace Alice's address with his own address, an error is thrown
const error = t.throws(() => {
	aliceContactObject.walletAddress = "54321-wallet-address-Bob-54321";
}, {instanceOf: TypeError});

//Bob is not able to change Alice's wallet address without getting an error
t.deepEqual(
    error?.message,
    "Cannot assign to read only property 'walletAddress' of object '[object Object]'"
);

The code above shows the usage of harden can avoid the problem in the initial piece of code. In this example, Alice first uses the harden function on the aliceContactObject object before sending it to Bob. Bob then attempts to change Alice’s walletAddress field in the aliceContactObject object to his own wallet address. This, however, throws a typeError with a message stating that the walletAddress property is a read-only property. It is thus impossible for Bob to change the walletAddress property without triggering a typeError from Agoric.

General rule

If entity e creates an object o and entity e passes object o across the trust boundary, then entity e should pass harden(o) instead of o [1].

Known uses

References

[1] Endo, Endo and hardened javascript (ses) programming guide

[2] Agoric, Glossary

[3] M. S. Miller and K. Sills, Why not to use object.freeze for immutable js objects?

[4] Agoric, Hardened javascript