Skip to content

Commit a28a30c

Browse files
authored
Merge pull request #8837 from BitGo/chalo-457-trx-account-create-canonical-encoding
fix(sdk-coin-trx): emit canonical AccountCreate raw_data_hex
2 parents ceb10dc + a9f8bae commit a28a30c

2 files changed

Lines changed: 38 additions & 3 deletions

File tree

modules/sdk-coin-trx/src/lib/accountCreateTxBuilder.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ import {
1515
} from './utils';
1616
import { ACCOUNT_CREATE_TYPE_URL } from './constants';
1717

18-
import ContractType = protocol.Transaction.Contract.ContractType;
19-
2018
export class AccountCreateTxBuilder extends TransactionBuilder {
2119
protected _signingKeys: BaseKey[];
2220
// Stored as hex address, consistent with _ownerAddress
@@ -148,8 +146,13 @@ export class AccountCreateTxBuilder extends TransactionBuilder {
148146
};
149147
const accountCreateContract = protocol.AccountCreateContract.fromObject(rawContract);
150148
const accountCreateContractBytes = protocol.AccountCreateContract.encode(accountCreateContract).finish();
149+
// AccountCreateContract is enum value 0 — the proto3 default. TRON's node
150+
// re-serializes raw_data from broadcast JSON and omits default-valued
151+
// fields, producing a different raw_data_hex (and txID) than the SDK if
152+
// we encode the type field explicitly. Skip it so signing and broadcast
153+
// hashes match. See freezeBalanceTxBuilder.ts:175-181 for the same class
154+
// of issue on the inner `resource` field.
151155
const txContract = {
152-
type: ContractType.AccountCreateContract,
153156
parameter: {
154157
value: accountCreateContractBytes,
155158
type_url: ACCOUNT_CREATE_TYPE_URL,

modules/sdk-coin-trx/test/unit/transactionBuilder/accountCreateTxBuilder.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,38 @@ describe('Tron AccountCreate builder', function () {
115115

116116
assert.equal(tx2.toJson().txID, originalTxId);
117117
});
118+
119+
// Regression test: AccountCreateContract's enum value is 0, which is the
120+
// proto3 default for the outer Transaction.Contract.type field. When the
121+
// SDK explicitly encoded `type: 0`, the wire format included the 2-byte
122+
// tag `0800` inside the contract envelope. TRON's node re-serializes
123+
// raw_data from broadcast JSON and omits default-valued fields, producing
124+
// a 2-byte-shorter canonical raw_data_hex and a different txID. The TSS
125+
// signature was valid for the SDK's hash but recovered to a garbage
126+
// pubkey under TRON's canonical hash, so AccountCreate broadcasts failed
127+
// with SIGERROR "signed by <random T...> but not contained of permission".
128+
it('produces raw_data_hex without the proto3 default-valued type field', async () => {
129+
const timestamp = 1779455020653;
130+
const expiration = 1779465820653;
131+
const txBuilder = initTxBuilder();
132+
txBuilder.timestamp(timestamp);
133+
txBuilder.expiration(expiration);
134+
const tx = await txBuilder.build();
135+
const rawDataHex = tx.toJson().raw_data_hex;
136+
137+
// The buggy encoding had `5a68 0800 1264` (contract tag, length 0x68,
138+
// type=0, parameter tag, length 0x64). The canonical encoding TRON
139+
// re-serializes to drops the `0800` and decrements the contract length
140+
// by 2 -> `5a66 1264`.
141+
assert.ok(
142+
!rawDataHex.includes('5a68080012'),
143+
`raw_data_hex must not include the proto3-default \`type: 0\` tag (0800). Got: ${rawDataHex}`
144+
);
145+
assert.ok(
146+
rawDataHex.includes('5a661264'),
147+
`raw_data_hex must use the canonical contract framing (5a66...1264). Got: ${rawDataHex}`
148+
);
149+
});
118150
});
119151

120152
describe('should validate', () => {

0 commit comments

Comments
 (0)