tronprotocol / tips

TRON Improvement Proposals
229 stars 203 forks source link

TIP-712: TRON typed structured data hashing and signing #443

Closed yanghang8612 closed 1 year ago

yanghang8612 commented 2 years ago
tip: 712
title: TRON typed structured data hashing and signing   
author: yanghang8612@163.com
discussions to: https://github.com/tronprotocol/TIPs/issues/443
status: Draft
type: Standards Track
category: Interface
created: 2022-07-25

Abstract

This TIP introduces a standard for hashing and signing of typed structured data as opposed to just bytestrings into TRON protocol to solve the problem that hashing structured data is non-trivial and errors result in loss of the security properties of the system, etc.

The encoding function, hashing algorithm and signing algorithm is identical to EIP-712. There are some differences in details. It includes a

Motivation

This TIP aims to improve the usability of off-chain message signing for use on-chain. We are seeing growing adoption of off-chain message signing as it saves transaction fees and reduces the number of transactions on the blockchain. Currently signed messages are an opaque hex string displayed to the user with little context about the items that make up the message.

A signature scheme consists of hashing algorithm and a signing algorithm. A good hashing algorithm should satisfy security properties such as determinism, second pre-image resistance and collision resistance. Otherwise, it may cause security problems or replay problems, etc. This standard aims to solve these problems.

Specifications

For the structured data 𝕊, they are encoded to bytestrings suitable for hashing and signing as follows:

The encoding is compliant with TIP-191 and it`s totaly compatible with EIP-712. The 'version byte' is fixed to 0x01, the version specific data is the 32-byte domain separator domainSeparator and the data to sign is the 32-byte hashStruct(message).

Definition of typed structured data 𝕊

Definitions for all types are identical to EIP-712.

Definition of hashStruct

The hashStruct function is defined as

It's totaly compatible with EIP-712.

Definition of encodeType

The type of a struct is encoded as name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")" where each member is written as type ‖ " " ‖ name.

It's totaly compatible with EIP-712.

For trcToken, it should be treated as atomic type.

struct AssetTransfer {
    address from;
    address to;
    trcToken id;
    uint256 amount;
    uint8 v;
    bytes32 r;
    bytes32 s;
}

For example, the above AssetTransfer struct is encoded as AssetTransfer(address from,address to,trcToken id,uint256 amount,uint8 v,bytes32 r,bytes32 s).

Definition of encodeData

The encoding of a struct instance is enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ), i.e. the concatenation of the encoded member values in the order that they appear in the type. Each encoded member value is exactly 32-byte long.

It's totaly compatible with EIP-712.

The only difference between TRON address and Ethereum address is that TRON address starts with a byte prefix 0x41 and uses base58 encoding, so prefix needs to be removed when the address type is processed.

Definition of domainSeparator

domainSeparator = hashStruct(tip712Domain)

where the type of tip712Domain is identical to EIP712Domain struct defined in EIP-712. It also contains one or more of the below fields.

The only difference is that we define the chainId data used in the domainSeparator to be equal to block.chainid & 0xffffffff. In other words, the chainId data used in the domainSeparator only take the higher four bytes of block.chainId.

The formula for calculating the chainid in domainSeparator is as follows:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract ChainIdExample {

    // after solidity_v0.8.0
    function getChainId() public view returns(bytes32,uint256) {
        uint256 chainId = block.chainid & 0xffffffff;
        return (bytes32(chainId), chainId);
    }

    // before solidity_v0.8.0
    function getChainIdAssembly() public view returns(bytes32,uint256) {
        uint256 chainId;
        assembly {
            chainId := and(chainid(), 0xffffffff)
        }
        return (bytes32(chainId), chainId);
    }
}

Mainnet TIP-712 chainId

In HEX: 0x000000000000000000000000000000000000000000000000000000002b6653dc

In Decimal: 728126428

Example Contract

Nile Testnet TIP-712 chainId

In HEX: 0x00000000000000000000000000000000000000000000000000000000cd8690dc

In Decimal: 3448148188

Example Contract

Backwards Compatibility

The FullNode gRPC calls and SomeStruct.typeHash parameter are currently undefined. Defining them should not affect the behaviour of existing DApps.

The Solidity expression keccak256(someInstance) for an instance someInstance of a struct type SomeStruct is valid syntax. It currently evaluates to the keccak256 hash of the memory address of the instance. This behaviour should be considered dangerous. In some scenarios it will appear to work correctly but in others it will fail determinism and/or injectiveness. DApps that depend on the current behaviour should be considered dangerously broken.

marc-aurele-besner commented 2 years ago

Hi,

I see the standard chaiId to use for mainnet and nile for EIP712, any standard for shista and tronex?

Thank you

yanghang8612 commented 1 year ago

Close this issue as there has been no new discussion for more than 6 months. The community has decided to adopt this issue. Check TIP detail at TIP-712.

ducnv300302 commented 1 year ago

can i have a example ? please