创建身份识别合约:如何高效校验重复邮箱、电话及合约地址?
Hey there! Great question—tackling duplicate prevention in identity-focused Solidity contracts while keeping gas costs and storage bloat in check is a super common challenge. Let’s break down some better alternatives to your current approach, along with why they work:
Person Contracts (Centralize Data in One Registry) Your original setup deploys a new Person contract for every user, which is extremely gas-heavy—each contract deployment costs significant ETH, plus you end up with scattered storage across dozens/hundreds of contracts. Instead, store all user data in a single registry contract using structs and arrays, with lightweight mappings to enforce uniqueness.
Here’s a refined example:
contract PersonRegistry { struct Person { uint dateCreated; string name; string email; string phone; address owner; } // Track uniqueness: hash of email/phone → index of person in the array mapping(bytes32 => uint) private emailToIndex; mapping(bytes32 => uint) private phoneToIndex; mapping(address => uint) private ownerToIndex; // Prevent duplicate addresses Person[] private registeredPeople; function register(string calldata _name, string calldata _email, string calldata _phone) external { bytes32 emailHash = keccak256(abi.encodePacked(_email)); bytes32 phoneHash = keccak256(abi.encodePacked(_phone)); // Validate no duplicates exist require(emailToIndex[emailHash] == 0, "Email already registered"); require(phoneToIndex[phoneHash] == 0, "Phone already registered"); require(ownerToIndex[msg.sender] == 0, "Address already linked to a person"); // Add new person to the array uint newIndex = registeredPeople.length; registeredPeople.push(Person({ dateCreated: block.timestamp, name: _name, email: _email, phone: _phone, owner: msg.sender })); // Update mappings (use +1 to avoid confusion with default 0 value) emailToIndex[emailHash] = newIndex + 1; phoneToIndex[phoneHash] = newIndex + 1; ownerToIndex[msg.sender] = newIndex + 1; } // Getter for retrieving a person by email function getPersonByEmail(string calldata _email) external view returns (Person memory) { bytes32 emailHash = keccak256(abi.encodePacked(_email)); uint index = emailToIndex[emailHash] - 1; require(index < registeredPeople.length, "Person not found"); return registeredPeople[index]; } // Add similar getters for phone/owner address }
Why this works:
- Way lower gas: No more deploying a contract per user—only one deployment, and storing data in an array is cheaper than managing multiple contract storages.
- Leaner mappings: Instead of storing full contract addresses (20 bytes), we store array indices (effectively pointers), and the mappings only track uniqueness, not full data.
- Easier management: All user data lives in one place, making it simpler to add features like updates or deletions later.
If you don’t need to retrieve the raw email/phone from the contract (or can offload that to off-chain storage), store hashes of email/phone instead of the full strings. Dynamic string types are more expensive to store than fixed-size bytes32 values.
Modify the struct like this:
struct Person { uint dateCreated; string name; bytes32 emailHash; bytes32 phoneHash; address owner; }
Then, when registering, you’d compute the hash upfront (either on-chain or off-chain) and store that. This cuts down on storage gas costs significantly, especially for longer emails/phone numbers.
If you’re planning to register many users at once, a Merkle Tree can drastically reduce on-chain storage. You’d precompute a tree of all unique email/phone hashes, store only the root hash in the contract, and let users submit a proof to verify their eligibility during registration.
This is great for gas efficiency in bulk scenarios, but it’s overkill for individual, one-off registrations since it requires off-chain proof generation.
Your initial mapping idea isn’t bad, but the real gas bloat comes from deploying individual Person contracts. Centralizing data in a single registry is the most practical, cost-effective solution for most identity verification use cases. Combine that with hashed values for email/phone, and you’ll keep both gas costs and storage size under control.
内容的提问来源于stack exchange,提问作者HLeite

