Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CU-86ducwq4y - BSNeo3 - Ledger - Fix issue where sending assets from … #88

Merged
merged 1 commit into from
Aug 14, 2024
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@cityofzion/bs-neo3",
"comment": "Fix issue where sending assets from NEON3 is being incorrectly flagged as arbitrary contract invocation",
"type": "patch"
}
],
"packageName": "@cityofzion/bs-neo3"
}
2 changes: 1 addition & 1 deletion packages/bs-neo3/src/BSNeo3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export class BSNeo3<BSCustomName extends string = string>
? u.BigInteger.fromDecimal(intent.amount, intent.tokenDecimals).toString()
: intent.amount,
},
{ type: 'Any', value: '' },
{ type: 'Any', value: null },
],
}
})
Expand Down
88 changes: 64 additions & 24 deletions packages/bs-neo3/src/NeonDappKitLedgerServiceNeo3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ import { wallet, api, u } from '@cityofzion/neon-js'
import { NeonParser } from '@cityofzion/neon-dappkit'
import EventEmitter from 'events'

enum LedgerStatus {
OK = 0x9000,
}

enum LedgerCommand {
GET_PUBLIC_KEY = 0x04,
SIGN = 0x02,
}

enum LedgerSecondParameter {
MORE_DATA = 0x80,
LAST_DATA = 0x00,
}

export class NeonDappKitLedgerServiceNeo3 implements LedgerService {
emitter: LedgerServiceEmitter = new EventEmitter() as LedgerServiceEmitter

Expand Down Expand Up @@ -39,32 +53,42 @@ export class NeonDappKitLedgerServiceNeo3 implements LedgerService {
try {
this.emitter.emit('getSignatureStart')

const bip44Buffer = this.toBip44Buffer(addressIndex)
await transport.send(0x80, 0x02, 0, 0x80, bip44Buffer, [0x9000])
await transport.send(0x80, 0x02, 1, 0x80, Buffer.from(NeonParser.numToHex(networkMagic, 4, true), 'hex'), [
0x9000,
])
const bip44 = this.#toBip44(addressIndex)
// Send the BIP44 account as first chunk
await this.#sendChunk(transport, LedgerCommand.SIGN, 0, LedgerSecondParameter.MORE_DATA, bip44)

// Send the network magic as second chunk
await this.#sendChunk(
transport,
LedgerCommand.SIGN,
1,
LedgerSecondParameter.MORE_DATA,
NeonParser.numToHex(networkMagic, 4, true)
)

const chunks = serializedTransaction.match(/.{1,510}/g) || []

for (let i = 0; i < chunks.length - 1; i++) {
await transport.send(0x80, 0x02, 2 + i, 0x80, Buffer.from(chunks[i], 'hex'), [0x9000])
// We plus 2 because we already sent 2 chunks before
const commandIndex = 2 + i
await this.#sendChunk(transport, LedgerCommand.SIGN, commandIndex, LedgerSecondParameter.MORE_DATA, chunks[i])
}

const response = await transport.send(
0x80,
0x02,
2 + chunks.length,
0x00,
Buffer.from(chunks[chunks.length - 1], 'hex'),
[0x9000]
// Again we plus 2 because we already sent 2 chunks before getting the chunks
const lastChunkIndex = 2 + chunks.length
const response = await this.#sendChunk(
transport,
LedgerCommand.SIGN,
lastChunkIndex,
LedgerSecondParameter.LAST_DATA,
chunks[chunks.length - 1]
)

if (response.length <= 2) {
throw new Error(`No more data but Ledger did not return signature!`)
}

const signature = this.derSignatureToHex(response.toString('hex'))
const signature = this.#derSignatureToHex(response.toString('hex'))

return signature
} finally {
Expand All @@ -73,28 +97,44 @@ export class NeonDappKitLedgerServiceNeo3 implements LedgerService {
}

async getPublicKey(transport: Transport, addressIndex = 0): Promise<string> {
const bip44Buffer = this.toBip44Buffer(addressIndex)

const result = await transport.send(0x80, 0x04, 0x00, 0x00, bip44Buffer, [0x9000])
const bip44 = this.#toBip44(addressIndex)

const result = await this.#sendChunk(
transport,
LedgerCommand.GET_PUBLIC_KEY,
0x00,
LedgerSecondParameter.LAST_DATA,
bip44
)
const publicKey = result.toString('hex').substring(0, 130)

return publicKey
}

private toBip44Buffer(addressIndex = 0, changeIndex = 0, accountIndex = 0) {
const accountHex = this.to8BitHex(accountIndex + 0x80000000)
const changeHex = this.to8BitHex(changeIndex)
const addressHex = this.to8BitHex(addressIndex)
#sendChunk(
transport: Transport,
command: LedgerCommand,
commandIndex: number,
secondParameter: LedgerSecondParameter,
chunk: string
) {
return transport.send(0x80, command, commandIndex, secondParameter, Buffer.from(chunk, 'hex'), [LedgerStatus.OK])
}

#toBip44(addressIndex = 0, changeIndex = 0, accountIndex = 0) {
const accountHex = this.#to8BitHex(accountIndex + 0x80000000)
const changeHex = this.#to8BitHex(changeIndex)
const addressHex = this.#to8BitHex(addressIndex)

return Buffer.from('8000002C' + '80000378' + accountHex + changeHex + addressHex, 'hex')
return '8000002C' + '80000378' + accountHex + changeHex + addressHex
}

private to8BitHex(num: number): string {
#to8BitHex(num: number): string {
const hex = num.toString(16)
return '0'.repeat(8 - hex.length) + hex
}

private derSignatureToHex(response: string): string {
#derSignatureToHex(response: string): string {
const ss = new u.StringStream(response)
// The first byte is format. It is usually 0x30 (SEQ) or 0x31 (SET)
// The second byte represents the total length of the DER module.
Expand Down
Loading