Skip to content

Commit

Permalink
add v2 txn support
Browse files Browse the repository at this point in the history
  • Loading branch information
chris124567 committed Oct 20, 2023
1 parent 417512a commit 31aa1ae
Show file tree
Hide file tree
Showing 12 changed files with 497 additions and 273 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ delete:
############

DEFINES += OS_IO_SEPROXYHAL
DEFINES += HAVE_PRINTF PRINTF=screen_printf
DEFINES += HAVE_SPRINTF
DEFINES += HAVE_IO_USB HAVE_L4_USBLIB IO_USB_MAX_ENDPOINTS=7 IO_HID_EP_LENGTH=64 HAVE_USB_APDU
DEFINES += HAVE_LEGACY_PID
Expand Down
8 changes: 1 addition & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,12 @@ go 1.20

require (
github.com/karalabe/hid v1.0.0
gitlab.com/NebulousLabs/Sia v1.5.6
go.sia.tech/core v0.1.12-0.20231011172826-6ca0ac7b3b6b
go.sia.tech/core v0.1.12-0.20231018000605-0e5549736580
lukechampine.com/flagg v1.1.1
)

require (
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/dchest/threefish v0.0.0-20120919164726-3ecf4c494abf // indirect
gitlab.com/NebulousLabs/encoding v0.0.0-20200604091946-456c3dc907fe // indirect
gitlab.com/NebulousLabs/errors v0.0.0-20200929122200-06c536cf6975 // indirect
gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40 // indirect
gitlab.com/NebulousLabs/merkletree v0.0.0-20200118113624-07fbf710afc4 // indirect
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 // indirect
golang.org/x/sys v0.5.0 // indirect
lukechampine.com/frand v1.4.2 // indirect
Expand Down
189 changes: 2 additions & 187 deletions go.sum

Large diffs are not rendered by default.

90 changes: 19 additions & 71 deletions sialedger.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"strconv"

"go.sia.tech/core/types"
"go.sia.tech/core/consensus"

"github.com/karalabe/hid"
"lukechampine.com/flagg"
Expand Down Expand Up @@ -266,70 +267,20 @@ func (n *Nano) SignHash(hash [32]byte, keyIndex uint32) (sig [64]byte, err error
return
}

func encodeV2Transaction(e *types.Encoder, txn *types.V2Transaction) {
e.WriteUint8(2)
e.WritePrefix(len(txn.SiacoinInputs))
for _, in := range txn.SiacoinInputs {
in.Parent.ID.EncodeTo(e)
}
e.WritePrefix(len(txn.SiacoinOutputs))
for _, out := range txn.SiacoinOutputs {
out.EncodeTo(e)
}
e.WritePrefix(len(txn.SiafundInputs))
for _, in := range txn.SiafundInputs {
in.Parent.ID.EncodeTo(e)
}
e.WritePrefix(len(txn.SiafundOutputs))
for _, out := range txn.SiafundOutputs {
out.EncodeTo(e)
}
e.WritePrefix(len(txn.FileContracts))
for _, fc := range txn.FileContracts {
fc.EncodeTo(e)
}
e.WritePrefix(len(txn.FileContractRevisions))
for _, fcr := range txn.FileContractRevisions {
fcr.Parent.ID.EncodeTo(e)
fcr.Revision.EncodeTo(e)
}
e.WritePrefix(len(txn.FileContractResolutions))
for _, fcr := range txn.FileContractResolutions {
fcr.Parent.ID.EncodeTo(e)
// normalize proof
if sp, ok := fcr.Resolution.(*types.V2StorageProof); ok {
c := *sp // don't modify original
c.ProofIndex.MerkleProof = nil
fcr.Resolution = &c
}
fcr.Resolution.(types.EncoderTo).EncodeTo(e)
}
e.WritePrefix(len(txn.Attestations))
for _, a := range txn.Attestations {
a.EncodeTo(e)
}
e.WriteBytes(txn.ArbitraryData)
e.WriteBool(txn.NewFoundationAddress != nil)
if txn.NewFoundationAddress != nil {
txn.NewFoundationAddress.EncodeTo(e)
}
txn.MinerFee.EncodeTo(e)
}

func (n *Nano) CalcTxnHash(txn *types.Transaction, v2Txn *types.V2Transaction, sigIndex uint16, changeIndex uint32) (hash [32]byte, err error) {
func (n *Nano) CalcTxnHash(txn *types.Transaction, v2txn *types.V2Transaction, sigIndex uint16, changeIndex uint32) (hash [32]byte, err error) {
var buf bytes.Buffer
e := types.NewEncoder(&buf)
binary.Write(&buf, binary.LittleEndian, uint32(0)) // keyIndex; ignored since we are not signing
binary.Write(&buf, binary.LittleEndian, sigIndex)
binary.Write(&buf, binary.LittleEndian, changeIndex)
if txn != nil {
// v2 = false
e.WriteBool(false)
e.WriteBool(v2txn != nil)
if v2txn == nil {
txn.EncodeTo(e)
} else if v2Txn != nil {
// v2 = true
e.WriteBool(true)
encodeV2Transaction(e, v2Txn)
} else {
fmt.Println("Expected:", consensus.State{}.InputSigHash(*v2txn))
fmt.Println("Expected:", consensus.State{}.InputSigHash(*v2txn))
fmt.Println("Expected:", consensus.State{}.InputSigHash(*v2txn))
types.V2TransactionSemantics(*v2txn).EncodeTo(e)
}
e.Flush()

Expand All @@ -350,20 +301,17 @@ func (n *Nano) CalcTxnHash(txn *types.Transaction, v2Txn *types.V2Transaction, s
return
}

func (n *Nano) SignTxn(txn *types.Transaction, v2Txn *types.V2Transaction, sigIndex uint16, keyIndex, changeIndex uint32) (sig [64]byte, err error) {
func (n *Nano) SignTxn(txn *types.Transaction, v2txn *types.V2Transaction, sigIndex uint16, keyIndex, changeIndex uint32) (sig [64]byte, err error) {
var buf bytes.Buffer
e := types.NewEncoder(&buf)
binary.Write(&buf, binary.LittleEndian, keyIndex)
binary.Write(&buf, binary.LittleEndian, sigIndex)
binary.Write(&buf, binary.LittleEndian, changeIndex)
if txn != nil {
// v2 = false
e.WriteBool(false)
e.WriteBool(v2txn != nil)
if v2txn == nil {
txn.EncodeTo(e)
} else if v2Txn != nil {
// v2 = true
e.WriteBool(true)
encodeV2Transaction(e, v2Txn)
} else {
types.V2TransactionSemantics(*v2txn).EncodeTo(e)
}
e.Flush()

Expand Down Expand Up @@ -619,10 +567,10 @@ func main() {
sigIndex := uint16(parseIndex(args[1]))

var txn *types.Transaction
var v2Txn *types.V2Transaction
var v2txn *types.V2Transaction
if *v2 {
v2Txn = new(types.V2Transaction)
if err := json.Unmarshal(txnBytes, &v2Txn); err != nil {
v2txn = new(types.V2Transaction)
if err := json.Unmarshal(txnBytes, &v2txn); err != nil {
log.Fatalln("Couldn't decode v2 transaction:", err)
}
} else {
Expand All @@ -633,13 +581,13 @@ func main() {
}

if *txnHash {
sighash, err := nano.CalcTxnHash(txn, v2Txn, sigIndex, uint32(*txnChangeIndex))
sighash, err := nano.CalcTxnHash(txn, v2txn, sigIndex, uint32(*txnChangeIndex))
if err != nil {
log.Fatalln("Couldn't get hash:", err)
}
fmt.Println(hex.EncodeToString(sighash[:]))
} else {
sig, err := nano.SignTxn(txn, v2Txn, sigIndex, parseIndex(args[2]), uint32(*txnChangeIndex))
sig, err := nano.SignTxn(txn, v2txn, sigIndex, parseIndex(args[2]), uint32(*txnChangeIndex))
if err != nil {
log.Fatalln("Couldn't get signature:", err)
}
Expand Down
18 changes: 13 additions & 5 deletions src/calcTxnHash.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
#include "blake2b.h"
#include "sia.h"
#include "sia_ux.h"
#include "txn.h"
#include "txn_wrapper.h"

static calcTxnHashContext_t *ctx = &global.calcTxnHashContext;

Expand Down Expand Up @@ -111,7 +111,7 @@ static unsigned int ui_calcTxnHash_elem_button(void) {
return 0;
}
// Attempt to decode the next element in the transaction.
switch (txn_next_elem(&ctx->txn)) {
switch (txn_wrapper_next_elem(&ctx->txn)) {
case TXN_STATE_ERR:
// The transaction is invalid.
io_exchange_with_code(SW_INVALID_PARAM, 0);
Expand Down Expand Up @@ -162,6 +162,7 @@ static void fmtTxnElem() {

switch (txn->elemType) {
case TXN_ELEM_SC_OUTPUT:
case V2TXN_ELEM_SC_OUTPUT:
memmove(ctx->labelStr, "SC Output #", 11);
bin2dec(ctx->labelStr + 11, txn->displayIndex);
// An element can have multiple screens. For each siacoin output, the
Expand All @@ -179,6 +180,7 @@ static void fmtTxnElem() {
break;

case TXN_ELEM_SF_OUTPUT:
case V2TXN_ELEM_SF_OUTPUT:
memmove(ctx->labelStr, "SF Output #", 11);
bin2dec(ctx->labelStr + 11, txn->displayIndex);
if (ctx->elemPart == 0) {
Expand All @@ -192,6 +194,8 @@ static void fmtTxnElem() {
break;

case TXN_ELEM_MINER_FEE:
case V2TXN_ELEM_MINER_FEE:
PRINTF("DISPLAYING MINER FEE!\n");
// Miner fees only have one part.
memmove(ctx->labelStr, "Miner Fee #", 11);
bin2dec(ctx->labelStr + 11, txn->sliceIndex);
Expand Down Expand Up @@ -240,7 +244,11 @@ void handleCalcTxnHash(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dat
uint32_t changeIndex = U4LE(dataBuffer, 0);
dataBuffer += 4;
dataLength -= 4;
txn_init(&ctx->txn, sigIndex, changeIndex);
ctx->txn.v2 = dataBuffer[0];
dataBuffer += 1;
dataLength -= 1;

txn_wrapper_init(&ctx->txn, sigIndex, changeIndex);

// Set ctx->sign according to P2.
ctx->sign = (p2 & P2_SIGN_HASH);
Expand All @@ -255,12 +263,12 @@ void handleCalcTxnHash(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dat
}

// Add the new data to transaction decoder.
txn_update(&ctx->txn, dataBuffer, dataLength);
txn_wrapper_update(&ctx->txn, dataBuffer, dataLength);

// Attempt to decode the next element of the transaction. Note that this
// code is essentially identical to ui_calcTxnHash_elem_button. Sadly,
// there doesn't seem to be a clean way to avoid this duplication.
switch (txn_next_elem(&ctx->txn)) {
switch (txn_wrapper_next_elem(&ctx->txn)) {
case TXN_STATE_ERR:
THROW(SW_INVALID_PARAM);
break;
Expand Down
13 changes: 10 additions & 3 deletions src/calcTxnHash_nbgl.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "sia.h"
#include "sia_ux.h"
#include "txn.h"
#include "txn_wrapper.h"

static calcTxnHashContext_t *ctx = &global.calcTxnHashContext;

Expand All @@ -27,6 +28,7 @@ static void fmtTxnElem(void) {

switch (txn->elemType) {
case TXN_ELEM_SC_OUTPUT:
case V2TXN_ELEM_SC_OUTPUT:
memmove(ctx->labelStr, "SC Output #", 11);
bin2dec(ctx->labelStr + 11, txn->displayIndex);
// An element can have multiple screens. For each siacoin output, the
Expand All @@ -44,6 +46,7 @@ static void fmtTxnElem(void) {
break;

case TXN_ELEM_SF_OUTPUT:
case V2TXN_ELEM_SF_OUTPUT:
memmove(ctx->labelStr, "SF Output #", 11);
bin2dec(ctx->labelStr + 11, txn->displayIndex);
if (ctx->elemPart == 0) {
Expand All @@ -57,6 +60,7 @@ static void fmtTxnElem(void) {
break;

case TXN_ELEM_MINER_FEE:
case V2TXN_ELEM_MINER_FEE:
// Miner fees only have one part.
memmove(ctx->labelStr, "Miner Fee #", 11);
bin2dec(ctx->labelStr + 11, txn->sliceIndex);
Expand Down Expand Up @@ -106,7 +110,7 @@ static bool nav_callback(uint8_t page, nbgl_pageContent_t *content) {
// Attempt to decode the next element of the transaction. Note that this
// code is essentially identical to ui_calcTxnHash_elem_button. Sadly,
// there doesn't seem to be a clean way to avoid this duplication.
switch (txn_next_elem(&ctx->txn)) {
switch (txn_wrapper_next_elem(&ctx->txn)) {
case TXN_STATE_ERR:
io_exchange_with_code(SW_INVALID_PARAM, 0);
return false;
Expand Down Expand Up @@ -195,7 +199,10 @@ void handleCalcTxnHash(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dat
uint32_t changeIndex = U4LE(dataBuffer, 0);
dataBuffer += 4;
dataLength -= 4;
txn_init(&ctx->txn, sigIndex, changeIndex);
ctx->txn.v2 = dataBuffer[0];
dataBuffer += 1;
dataLength -= 1;
txn_wrapper_init(&ctx->txn, sigIndex, changeIndex);

// Set ctx->sign according to P2.
ctx->sign = (p2 & P2_SIGN_HASH);
Expand All @@ -210,7 +217,7 @@ void handleCalcTxnHash(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dat
}

// Add the new data to transaction decoder.
txn_update(&ctx->txn, dataBuffer, dataLength);
txn_wrapper_update(&ctx->txn, dataBuffer, dataLength);

*flags |= IO_ASYNCH_REPLY;
nbgl_useCaseRegularReview(0, 0, "Cancel", NULL, nav_callback, confirm_callback);
Expand Down
1 change: 1 addition & 0 deletions src/txn.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ txnDecoderState_e txn_next_elem(txn_state_t *txn) {

void txn_init(txn_state_t *txn, uint16_t sigIndex, uint32_t changeIndex) {
memset(txn, 0, sizeof(txn_state_t));
txn->v2 = false;
txn->buflen = txn->pos = txn->sliceIndex = txn->displayIndex = txn->sliceLen = txn->valLen = 0;
txn->elemType = -1; // first increment brings it to SC_INPUT
txn->sigIndex = sigIndex;
Expand Down
14 changes: 14 additions & 0 deletions src/txn.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,25 @@ typedef enum {
TXN_ELEM_MINER_FEE,
TXN_ELEM_ARB_DATA,
TXN_ELEM_TXN_SIG,

V2TXN_ELEM_SC_INPUT,
V2TXN_ELEM_SC_OUTPUT,
V2TXN_ELEM_SF_INPUT,
V2TXN_ELEM_SF_OUTPUT,
V2TXN_ELEM_FC,
V2TXN_ELEM_FC_REVISIONS,
V2TXN_ELEM_FC_RESOLUTIONS,
V2TXN_ELEM_ATTESTATIONS,
V2TXN_ELEM_ARB_DATA,
V2TXN_ELEM_FDN_ADDR,
V2TXN_ELEM_MINER_FEE,
} txnElemType_e;

// txn_state_t is a helper object for computing the SigHash of a streamed
// transaction.
typedef struct {
uint8_t v2;

uint8_t buf[510]; // holds raw tx bytes; large enough for two 0xFF reads
uint16_t buflen; // number of valid bytes in buf
uint16_t pos; // mid-decode offset; reset to 0 after each elem
Expand Down
28 changes: 28 additions & 0 deletions src/txn_wrapper.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "txn_wrapper.h"

#include "txn.h"
#include "v2txn.h"

void txn_wrapper_init(txn_state_t *txn, uint16_t sigIndex, uint32_t changeIndex) {
if (txn->v2) {
v2txn_init(txn, sigIndex, changeIndex);
} else {
txn_init(txn, sigIndex, changeIndex);
}
}

void txn_wrapper_update(txn_state_t *txn, uint8_t *in, uint8_t inlen) {
if (txn->v2) {
v2txn_update(txn, in, inlen);
} else {
txn_update(txn, in, inlen);
}
}

txnDecoderState_e txn_wrapper_next_elem(txn_state_t *txn) {
if (txn->v2) {
return v2txn_next_elem(txn);
} else {
return txn_next_elem(txn);
}
}
26 changes: 26 additions & 0 deletions src/txn_wrapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#ifndef TXN_WRAPPER_H
#define TXN_WRAPPER_H

#include "txn.h"

// Wrappers so that the appropriate function is called for updating
// transaction state depending on if the transaction is v1 or v2.

// txn_wrapper_init initializes a transaction decoder, preparing it to calculate the
// requested SigHash. It will call txn_init if given a transaction with v2=false and
// v2txn_init if given a transaction where v2=true.
void txn_wrapper_init(txn_state_t *txn, uint16_t sigIndex, uint32_t changeIndex);

// txn_wrapper_update adds data to a transaction decoder. It will call txn_update if given a transaction with v2=false and
// v2txn_update if given a transaction where v2=true.
void txn_wrapper_update(txn_state_t *txn, uint8_t *in, uint8_t inlen);

// txn_wrapper_next_elem decodes the next element of the transaction. If the
// element is ready for display, txn_next_elem returns TXN_STATE_READY. If more
// data is required, it returns TXN_STATE_PARTIAL. If a decoding error is
// encountered, it returns TXN_STATE_ERR. If the transaction has been fully
// decoded, it returns TXN_STATE_FINISHED.
txnDecoderState_e txn_wrapper_next_elem(txn_state_t *txn);


#endif /* TXN_WRAPPER_H */
Loading

0 comments on commit 31aa1ae

Please sign in to comment.