Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions modules/sdk-coin-trx/src/lib/accountCreateTxBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ import {
} from './utils';
import { ACCOUNT_CREATE_TYPE_URL } from './constants';

import ContractType = protocol.Transaction.Contract.ContractType;

export class AccountCreateTxBuilder extends TransactionBuilder {
protected _signingKeys: BaseKey[];
// Stored as hex address, consistent with _ownerAddress
Expand Down Expand Up @@ -148,8 +146,13 @@ export class AccountCreateTxBuilder extends TransactionBuilder {
};
const accountCreateContract = protocol.AccountCreateContract.fromObject(rawContract);
const accountCreateContractBytes = protocol.AccountCreateContract.encode(accountCreateContract).finish();
// AccountCreateContract is enum value 0 — the proto3 default. TRON's node
// re-serializes raw_data from broadcast JSON and omits default-valued
// fields, producing a different raw_data_hex (and txID) than the SDK if
// we encode the type field explicitly. Skip it so signing and broadcast
// hashes match. See freezeBalanceTxBuilder.ts:175-181 for the same class
// of issue on the inner `resource` field.
const txContract = {
type: ContractType.AccountCreateContract,
parameter: {
value: accountCreateContractBytes,
type_url: ACCOUNT_CREATE_TYPE_URL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,38 @@ describe('Tron AccountCreate builder', function () {

assert.equal(tx2.toJson().txID, originalTxId);
});

// Regression test: AccountCreateContract's enum value is 0, which is the
// proto3 default for the outer Transaction.Contract.type field. When the
// SDK explicitly encoded `type: 0`, the wire format included the 2-byte
// tag `0800` inside the contract envelope. TRON's node re-serializes
// raw_data from broadcast JSON and omits default-valued fields, producing
// a 2-byte-shorter canonical raw_data_hex and a different txID. The TSS
// signature was valid for the SDK's hash but recovered to a garbage
// pubkey under TRON's canonical hash, so AccountCreate broadcasts failed
// with SIGERROR "signed by <random T...> but not contained of permission".
it('produces raw_data_hex without the proto3 default-valued type field', async () => {
const timestamp = 1779455020653;
const expiration = 1779465820653;
const txBuilder = initTxBuilder();
txBuilder.timestamp(timestamp);
txBuilder.expiration(expiration);
const tx = await txBuilder.build();
const rawDataHex = tx.toJson().raw_data_hex;

// The buggy encoding had `5a68 0800 1264` (contract tag, length 0x68,
// type=0, parameter tag, length 0x64). The canonical encoding TRON
// re-serializes to drops the `0800` and decrements the contract length
// by 2 -> `5a66 1264`.
assert.ok(
!rawDataHex.includes('5a68080012'),
`raw_data_hex must not include the proto3-default \`type: 0\` tag (0800). Got: ${rawDataHex}`
);
assert.ok(
rawDataHex.includes('5a661264'),
`raw_data_hex must use the canonical contract framing (5a66...1264). Got: ${rawDataHex}`
);
});
});

describe('should validate', () => {
Expand Down
Loading