什么是 public、external、internal 和 private?合约函数可见性怎么选
Solidity 四种函数可见性决定调用边界,却不等于访问控制或链上保密。本文讲清外部调用、继承、自调用、ABI 与代理风险。
一份合约把某个函数标成 private,有人便说“外部看不到,很安全”;另一个函数写着 public,又有人理解成“所有人都能随便操作”。这两种说法都混淆了两个问题:代码从哪里能调用,以及调用者是否满足业务权限。
Solidity 函数有四种可见性:external、public、internal、private。它们定义调用边界和继承范围,却不会自动完成管理员鉴权,也不会把链上数据变成秘密。
可见性回答“调用入口在哪里”,访问控制回答“谁被允许执行”。
external:面向合约外部的入口
external 函数属于合约外部接口,可以被交易或其他合约调用。当前合约不能直接用 f() 方式内部调用它;写 this.f() 会产生一次真正的外部消息调用,改变调用上下文并增加开销与风险。
public:外部和内部都能调用
public 函数也出现在外部接口中,同时允许合约内部直接调用。它并不表示任何地址都能成功完成操作,函数体仍可通过角色、签名、余额和状态条件拒绝调用。

internal:当前合约与派生合约可用
internal 函数不会成为普通外部 ABI 入口,只能由当前合约或继承它的合约内部调用。因为不暴露给外部 ABI,它还能接收 mapping 或 storage 引用等内部类型。internal 常用于复用核心逻辑,再由不同 public 或 external 入口做权限和参数检查。
private:只限定义它的合约
private 函数不能被派生合约直接调用,但这不是安全边界的全部。部署后的字节码公开可得,交易和状态变化也能观察。private 主要帮助代码封装,不能用于隐藏算法、密钥或商业秘密。
| 可见性 | 外部调用 | 当前合约直接调用 | 派生合约调用 |
|---|---|---|---|
| external | 可以 | 不能直接调用 | 作为外部调用处理 |
| public | 可以 | 可以 | 可以 |
| internal | 不可以 | 可以 | 可以 |
| private | 不可以 | 可以 | 不可以 |
this.f() 不是普通内部调用
通过 this 调用 public 或 external 函数,会从当前合约地址向自己发起外部调用。新的调用帧里 msg.sender 可能变成合约自身,还会经过 ABI 编解码。若权限逻辑没有考虑这种差异,行为可能与直接调用完全不同。
可见性不等于访问控制
提款函数即使 external,也可以要求 msg.sender 是管理员;内部函数即使 internal,仍可能被一个没有鉴权的 public 入口间接触发。审计必须沿所有外部入口追踪到敏感状态变化,不能看到 private 或 internal 就停止。
Private 状态变量同样不保密
状态变量可见性略有不同:public 会由编译器生成 getter,internal 可供派生合约访问,private 不供派生合约直接访问。但链上 storage 对观察者并非秘密,知道布局的人仍可能从节点数据解析内容。

Public 变量只自动生成读取器
public 状态变量会获得外部 getter,但不会自动生成 setter。外部可以读取不等于外部可以直接改写。对于数组和复杂结构,自动 getter 也可能只支持按索引或键查询,不一定返回完整对象。
ABI 会暴露哪些函数
public 和 external 函数属于合约外部接口,通常出现在 ABI;internal 和 private 不作为外部入口出现。不过通过 public 函数触发的内部逻辑仍属于交易行为。ABI 缺少某个函数名,不代表相关逻辑永远无法执行。
继承会改变可调用范围
派生合约能直接使用 internal 成员,却不能直接使用父合约 private 成员。函数重写还受 virtual、override 和可见性规则影响。阅读最终部署合约时,要把继承层次展开,确认真正执行的是哪一版实现。
代理合约让入口更间接
代理可以通过 fallback 和 delegatecall 把外部调用交给实现合约。用户面对的是代理地址,可见性与权限逻辑却来自当前实现。因此既要检查 ABI 和实现版本,也要检查升级权与初始化状态。
开发者如何选择
- 只打算供外部调用的接口优先考虑 external。
- 既需外部也需内部复用时使用 public,或拆成 external 入口加 internal 核心。
- 供继承体系复用的实现细节使用 internal。
- 仅当前合约需要的辅助逻辑可用 private,但不要把它当保密工具。
- 所有敏感外部路径都要独立做访问控制与状态检查。
审计检查清单
- 列出全部 public、external、receive 和 fallback 入口。
- 标出每个入口能修改的资金、权限和关键状态。
- 沿内部调用追踪到 internal 与 private 函数。
- 验证角色检查是否能被代理、自调用或回调绕过。
- 检查 public getter 是否意外暴露敏感业务信息。
- 在最终继承和代理实现上复核,而非只看单个文件。
官方资料
本文依据 Solidity:Function Visibility 与同页的 State Variable Visibility、Getter Functions。实际开发请以项目锁定编译器版本对应文档为准。
四种可见性是代码结构工具,不是完整安全模型。先找所有外部入口,再追踪它们能触达的内部逻辑和权限条件,才能判断一个操作究竟对谁开放、会改变什么。
本文为链上指南原创科普内容,不构成任何投资建议。智能合约可见性、权限和代理结构均可能影响资产安全,交互前请核对验证源码与独立审计。