Skip to content

Commit 9763931

Browse files
feat: normalize exit codes for ownable and merkle multi proof
1 parent 3b47b74 commit 9763931

File tree

10 files changed

+129
-53
lines changed

10 files changed

+129
-53
lines changed

contracts/contracts/lib/access/ownable_2step.tolk

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
import "./../utils.tolk";
22

3-
// Exit codes
4-
5-
// TODO: follow namespaced exitcodes structure
6-
/// Matches the exit code for `TactExitCodeAccessDenied` from `@stdlib`.
7-
const ERROR_ONLY_CALLABLE_BY_OWNER = 132;
8-
9-
/// Custom exit code indicating that the new owner is the same as the current one.
10-
const ERROR_CANNOT_TRANSFER_TO_SELF = 1001;
3+
// getFacilityId(stringCrc32(com.chainlink.ton.lib.access.Ownable2Step))
4+
const Ownable2Step_FACILITY_ID = 204;
5+
const Ownable2Step_ERROR_CODE = Ownable2Step_FACILITY_ID * 100;
116

12-
/// Custom exit code indicating that only the proposed (pending) owner may accept ownership.
13-
const ERROR_MUST_BE_PROPOSED_OWNER = 1002;
7+
// Exit codes
8+
enum Ownable2Step_Error {
9+
OnlyCallableByOwner = Ownable2Step_ERROR_CODE; // only the current owner may call this function.
10+
CannotTransferToSelf, // the new owner is the same as the current one.
11+
MustBeProposedOwner, // only the proposed (pending) owner may accept ownership.
12+
}
1413

1514
/// Ownable2Step trait provides ownership two-step transfer functionality.
1615
struct Ownable2Step {
@@ -98,7 +97,7 @@ fun Ownable2Step.onInternalMessage(mutate self, sender: address, body: slice): b
9897
/// #### Exit codes
9998
/// * 132: ERROR_ONLY_CALLABLE_BY_OWNER
10099
fun Ownable2Step.requireOwner(self, sender: address) {
101-
assert(sender == self.owner, ERROR_ONLY_CALLABLE_BY_OWNER);
100+
assert(sender == self.owner, Ownable2Step_Error.OnlyCallableByOwner);
102101
}
103102

104103
/// Initiates ownership transfer to a new address.
@@ -108,7 +107,7 @@ fun Ownable2Step.requireOwner(self, sender: address) {
108107
/// * 1001: ERROR_CANNOT_TRANSFER_TO_SELF
109108
fun Ownable2Step.transferOwnership(mutate self, sender: address, to: address) {
110109
self.requireOwner(sender);
111-
assert(to != self.owner, ERROR_CANNOT_TRANSFER_TO_SELF);
110+
assert(to != self.owner, Ownable2Step_Error.CannotTransferToSelf);
112111

113112
self.pendingOwner = to;
114113
}
@@ -118,8 +117,8 @@ fun Ownable2Step.transferOwnership(mutate self, sender: address, to: address) {
118117
/// #### Exit codes
119118
/// * 1002: ERROR_MUST_BE_PROPOSED_OWNER
120119
fun Ownable2Step.acceptOwnership(mutate self, sender: address) {
121-
assert(self.pendingOwner != null, ERROR_MUST_BE_PROPOSED_OWNER);
122-
assert(sender == self.pendingOwner, ERROR_MUST_BE_PROPOSED_OWNER);
120+
assert(self.pendingOwner != null, Ownable2Step_Error.MustBeProposedOwner);
121+
assert(sender == self.pendingOwner, Ownable2Step_Error.MustBeProposedOwner);
123122

124123
var previousOwner = self.owner;
125124
self.owner = sender;

contracts/contracts/lib/crypto/merkle_multi_proof.tolk

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import "../utils.tolk"
22

3+
// getFacilityId(stringCrc32(com.chainlink.ton.lib.crypto.MerkleMultiProof))
4+
const MerkleMultiProof_FACILITY_ID= 462;
5+
const MerkleMultiProof_ERROR_CODE= MerkleMultiProof_FACILITY_ID * 100;
6+
37
// --- Errror Codes ---
48

5-
const ERROR_INVALID_PROOF_LEAVES_CANNOT_BE_EMPTY: int = 1001;
6-
const ERROR_INVALID_PROOF_LEAVES_TOO_LARGE: int = 1002;
7-
const ERROR_INVALID_PROOF_PROOFS_TOO_LARGE: int = 1003;
8-
const ERROR_INVALID_PROOF_TOTAL_HASHES_EXCEEDED_MAX: int = 1004;
9-
const ERROR_INVALID_PROOF_DATA_SIZE_MISMATCH: int = 1005;
9+
enum MerkleMultiProof_Error {
10+
InvalidProofLeavesCannotBeEmpty = MerkleMultiProof_ERROR_CODE,
11+
InvalidProofLeavesTooLarge,
12+
InvalidProofProofsTooLarge,
13+
InvalidProofTotalHashesExceededMax,
14+
}
1015

1116
/// @notice internal domain separator, should be used as the first 32 bytes of an internal node's preimage.
1217
const INTERNAL_DOMAIN_SEPARATOR: int = 0x0000000000000000000000000000000000000000000000000000000000000001;
@@ -24,12 +29,12 @@ fun merkleRoot<I>(
2429
val leavesLen = leaves.size(32);
2530
val proofsLen = proofs.size(32);
2631

27-
assert (leavesLen != 0, ERROR_INVALID_PROOF_LEAVES_CANNOT_BE_EMPTY);
28-
assert (leavesLen <= MAX_NUM_HASHES, ERROR_INVALID_PROOF_LEAVES_TOO_LARGE + 1);
29-
assert (proofsLen <= MAX_NUM_HASHES, ERROR_INVALID_PROOF_PROOFS_TOO_LARGE + 1);
32+
assert (leavesLen != 0, MerkleMultiProof_Error.InvalidProofLeavesCannotBeEmpty);
33+
assert (leavesLen <= MAX_NUM_HASHES, MerkleMultiProof_Error.InvalidProofLeavesTooLarge as int + 1);
34+
assert (proofsLen <= MAX_NUM_HASHES, MerkleMultiProof_Error.InvalidProofProofsTooLarge as int + 1);
3035

3136
var totalHashes: int = leavesLen + proofsLen - 1;
32-
assert (totalHashes <= MAX_NUM_HASHES, ERROR_INVALID_PROOF_TOTAL_HASHES_EXCEEDED_MAX);
37+
assert (totalHashes <= MAX_NUM_HASHES, MerkleMultiProof_Error.InvalidProofTotalHashesExceededMax);
3338

3439
if (totalHashes == 0) {
3540
return leaves.next();

contracts/tests/lib/access/Ownable2Step.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ import { compile } from '@ton/blueprint'
55

66
import * as ownable2Step from '../../../wrappers/libraries/access/Ownable2Step'
77
import * as counter from '../../../wrappers/examples/Counter'
8+
import { facilityId } from '../../../wrappers/utils'
9+
import { crc32 } from 'zlib'
10+
11+
describe('Ownable2Step Unit Tests', () => {
12+
it('should match facility ID', async () => {
13+
expect(ownable2Step.FACILITY_ID).toBe(facilityId(crc32(ownable2Step.FACILITY_NAME)))
14+
})
15+
})
816

917
describe('Ownable2Step Counter', () => {
1018
let blockchain: Blockchain

contracts/tests/lib/merkle_proof/MerkleMultiProof.spec.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox'
22
import { toNano, Cell } from '@ton/core'
3-
import {
4-
MerkleMultiProofCalculator,
5-
MerkleMultiProofCalculatorStorage,
6-
} from '../../../wrappers/libraries/merkle_proof/MerkleMultiProofCalculator'
73
import { sha256_sync } from '@ton/crypto'
8-
94
import '@ton/test-utils'
10-
import { MerkleHelper, HashFunction } from './helpers/MerkleMultiProofHelper'
5+
6+
import { crc32 } from 'zlib'
117
import { loadContractCode } from '../../../wrappers/codeLoader'
128
import { asSnakeDataUint } from '../../../src/utils'
13-
import { TestVector, testVectors } from './TestVectors'
9+
import { testVectors } from './TestVectors'
1410
import { keccak256 } from '@ethersproject/keccak256'
1511

12+
import { MerkleHelper, HashFunction } from './helpers/MerkleMultiProofHelper'
13+
import * as mmp from '../../../wrappers/libraries/merkle_proof/MerkleMultiProof'
14+
import { facilityId } from '../../../wrappers/utils'
15+
import {
16+
MerkleMultiProofCalculator,
17+
MerkleMultiProofCalculatorStorage,
18+
} from '../../../wrappers/libraries/merkle_proof/MerkleMultiProofCalculator'
19+
20+
describe('MerkleMultiProof Unit Tests', () => {
21+
it('should match facility ID', async () => {
22+
expect(mmp.FACILITY_ID).toBe(facilityId(crc32(mmp.FACILITY_NAME)))
23+
})
24+
})
25+
1626
describe('MerkleMultiProofTests', () => {
1727
let blockchain: Blockchain
1828
let deployer: SandboxContract<TreasuryContract>

contracts/wrappers/libraries/access/Ownable2Step.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ import { crc32 } from 'zlib'
1313
import { CellCodec } from '../../utils'
1414
import { Maybe } from '@ton/core/dist/utils/maybe'
1515

16+
export const FACILITY_NAME = 'com.chainlink.ton.lib.access.Ownable2Step'
17+
export const FACILITY_ID = 204
18+
export const ERROR_CODE = FACILITY_ID * 100
19+
1620
export enum Errors {
17-
OnlyCallableByOwner = 132,
18-
CannotTransferToSelf = 1001,
19-
MustBeProposedOwner = 1002,
21+
OnlyCallableByOwner = ERROR_CODE,
22+
CannotTransferToSelf,
23+
MustBeProposedOwner,
2024
}
2125

2226
// @dev Message sent by the owner to transfer ownership of a contract.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export const FACILITY_NAME = 'com.chainlink.ton.lib.crypto.MerkleMultiProof'
2+
export const FACILITY_ID = 462
3+
export const ERROR_CODE = FACILITY_ID * 100
4+
5+
export enum Errors {
6+
InvalidProofLeavesCannotBeEmpty = ERROR_CODE,
7+
InvalidProofLeavesTooLarge,
8+
InvalidProofProofsTooLarge,
9+
InvalidProofTotalHashesExceededMax,
10+
}

pkg/ccip/bindings/merkleroot/exitcode_string.go

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/ccip/bindings/merkleroot/merkle_root.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"math/big"
55

66
"github.com/xssnick/tonutils-go/address"
7+
8+
"github.com/smartcontractkit/chainlink-ton/pkg/ton/tvm"
79
)
810

911
type Storage struct {
@@ -15,3 +17,23 @@ type Storage struct {
1517
MessageStates *big.Int `tlb:"## 128"`
1618
DeliveredMessageCount uint16 `tlb:"## 16"`
1719
}
20+
21+
//go:generate go run golang.org/x/tools/cmd/stringer@v0.38.0 -type=ExitCode
22+
type ExitCode tvm.ExitCode
23+
24+
var ExitCodeCodec tvm.ExitCodeCodecInt[ExitCode] = ExitCode(tvm.ExitCode(-1))
25+
26+
func (ExitCode) NewFrom(ec tvm.ExitCode) (ExitCode, error) {
27+
const (
28+
ecMin = int32(ErrorInvalidProofLeavesCannotBeEmpty)
29+
ecMax = int32(ErrorInvalidProofTotalHashesExceededMax)
30+
)
31+
return tvm.NewExitCodeInRange(ExitCode(ec), ecMin, ecMax)
32+
}
33+
34+
const (
35+
ErrorInvalidProofLeavesCannotBeEmpty ExitCode = iota + 20400
36+
ErrorInvalidProofLeavesTooLarge
37+
ErrorInvalidProofProofsTooLarge
38+
ErrorInvalidProofTotalHashesExceededMax
39+
)

pkg/ccip/bindings/ownable2step/exitcode_string.go

Lines changed: 8 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/ccip/bindings/ownable2step/types.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func (ExitCode) NewFrom(ec tvm.ExitCode) (ExitCode, error) {
4141
}
4242

4343
const (
44-
ErrorOnlyCallableByOwner ExitCode = ExitCode(132)
45-
ErrorCannotTransferToSelf = ExitCode(1001)
46-
ErrorMustBeProposedOwner = ExitCode(1002)
44+
ErrorOnlyCallableByOwner ExitCode = iota + 20400
45+
ErrorCannotTransferToSelf
46+
ErrorMustBeProposedOwner
4747
)

0 commit comments

Comments
 (0)