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

Implement Transaction.decode/1 function #98

Open
robrobbins opened this issue Mar 13, 2024 · 12 comments
Open

Implement Transaction.decode/1 function #98

robrobbins opened this issue Mar 13, 2024 · 12 comments
Labels
enhancement New feature or request good first issue Good for newcomers

Comments

@robrobbins
Copy link

I've a use case where Raw Transactions are signed via EthersJS (v6) for later broadcast.

The broadcasting (and all the other tooling, including a mock blockchain test env) is done via an Elixir (Phoenix) app.

I would like to be able to take the raw hex string returned by EthersJS (which is broadcast via RPC) and recover the public key (and address) via this project.

In my testing so far i have been able to successfully rebuild the transaction (it is a legacy transfer smart contract call) via ExRlp and a healthy dose of Base.encode16... i.e {nonce, gasPrice, gasLimit, to, amount} i can extract out fairly simply. Where I am failing is being able to use ExSecp2561k to recover the from.. there are so many variables here. I suspected it was my ability to correctly assemble the unsigned TX and pass that to 256.recover/4 along with v,r,s... Started using This libraries Ethers.Transaction and it's encode method - but i'm still not ever recovering the correct public key

I'm also happy to help work on this, but as I stand I'm stuck 😵‍💫

thoughts?

@alisinabh
Copy link
Member

Unfortunately as of right now Ethers does not provide a decode functionality. Generally a Transaction.decode/1 function is desired for cases like yours.

I think one reason that you were not able to do that is the specifics of EIP-155 and replay attack prevention solution. I have created this livebook for you which does what you want. Let me know if it helps you.

If you have the time, it would be awesome if you can invest the time and implement a decode function inside ethers.

@alisinabh alisinabh changed the title Request: Address recovery from an EthersJS signed Transaction Implement Transaction.decode/1 function Mar 13, 2024
@alisinabh alisinabh added enhancement New feature or request good first issue Good for newcomers labels Mar 13, 2024
@robrobbins
Copy link
Author

sweet! Let me go land this little feature and then i'll get a PR started.

@robrobbins
Copy link
Author

OK. With a minor adjustment to the handling of the v_int (when its 28) this looks to work. GREAT. LFG!..

I made some comments over on the livebook for things i noted while working with a few different signed transactions. Let me know your thoughts on those and we can make a plan that i'll begin on Transaction.decode

@alisinabh
Copy link
Member

Yeah sorry I just created that livebook last night and did not check it against multiple transactions.

Re those prefixes, they are defined in transaction envelope EIP. Right now there are 4 different types.

Access lists are also handled by RLP encoding. No extra actions required there.

Good to see that it worked for you. Looking forward to a PR with the decoder 😄

@robrobbins
Copy link
Author

I'll likely post issue as a follow up, which i can do later for decoding the input data. I'm handling transfer events exclusively so it's a simple matter to strip off the ID then split the hex string for recipient , amount... not sure how ethersjs does it (or if it does) but it might be nice to provide data parsing for known ERC20 methods.

@robrobbins
Copy link
Author

getting started on #100 . got to go put out other fires rest of the day, but i'll keep hacking as i get time.

@robrobbins
Copy link
Author

robrobbins commented Apr 5, 2024

gunna get some time back in here today. @alisinabh i think to be useful i'll need both the present legacy -- and more importantly the same for type2.

playing around with the decode steps from a signed type:2 tx from ethers.js i get a different output vector -> i.e. the list has to be parsed for [chain_id, max_fee_* etc...]

going to have to spend the time figuring out this returned list, and particularly how to assemble the {tx, parity} for the recovery.

i believe that's the only diff however, the members of that returned list.

oh, and, i suppose we don't have to figure out the y_parity as i believe it's returned in the initial decode? i could be wrong there

@robrobbins
Copy link
Author

robrobbins commented Apr 8, 2024

SMH. While i kind of suspect the access_list, I'm still scratching my head here on why a decoded type2 signed tx won't recover the correct address:

raw: 0x02f8a70103020482a41094826180541412d574cf1336d22c0c0a287822678a80b844a9059cbb000000000000000000000000b1228c21546e3848498169f4241ab63279d502d5000000000000000000000000000000000000000000000000000000e8d4a51000c080a0b37b7057eaf42412f834b4e3f1cd00ed4a346381a
c2bdd7ddc2fad48619453b3a00868d3423b2e365793bc691be1f7611e3d09c8e883535a1d7f25c2d2f8ee514c

^ random tx (transfer) generated and signed in a test with ethers.js (NOTE: i slice off '0x02' here..)

hex_decode! |> rlp_decode(:binary) will get you

[
<<1>>,
<<3>>,
<<2>>,
<<4>>,
<<164, 16>>,
<<130, 97, 128, 84, 20, 18, 213, 116, 207, 19, 54, 210, 44, 12, 10, 40, 120,
34, 103, 138>>,
"",
<<169, 5, 156, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, 34, 140, 33, 84,
110, 56, 72, 73, 129, 105, 244, 36, 26, 182, 50, 121, 213, 2, 213, 0, 0, 0,
0, 0, 0, ...>>,
[],
"",
<<179, 123, 112, 87, 234, 244, 36, 18, 248, 52, 180, 227, 241, 205, 0, 237,
74, 52, 99, 129, 172, 43, 221, 125, 220, 47, 173, 72, 97, 148, 83, 179>>,
<<8, 104, 211, 66, 59, 46, 54, 87, 147, 188, 105, 27, 225, 247, 97, 30, 61, 9,
200, 232, 131, 83, 90, 29, 127, 37, 194, 210, 248, 238, 81, 76>>
]

this in deconstructed as [chainId, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, data, access_list, y_parity, r, s]

you can slice the first 9 items and rlp_encode them (i have a feeling it goes wrong here) |> hash them then try to ecrecover them with y_parity of 0, r and s. i get a pub key/address but they are not correct.

using ethersjs i can see the recovered key of the above should be 0xc503d317994D03dD708492e3Ef16A9F210b3DECF

like the legacy decode -- this is prob just some formatting - but minus the 155 fuckery i thought 1559 would be easier.. lol..

@alisinabh you got any experience trying to decode 1559?

@robrobbins
Copy link
Author

or... it's putting the type back into the re-assembled digest somehow? uuugh. so many variables...

@alisinabh
Copy link
Member

I think you might just forgot to prepend the EIP-155 payload or something like that. Here is a livebook example with the payload you provided with the correct recovered address.

https://gist.github.com/alisinabh/249bf2c33bf288d69f7b18382d0494ac

@robrobbins
Copy link
Author

robrobbins commented Apr 9, 2024

I just got finished shouting and jumping up and down when i tried <<2>> <> rlp_encoded_digest AND IT WORKED
lemme go look at what u had

@robrobbins
Copy link
Author

yeah it was this |> then(&(<<2>> <> &1))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

2 participants