Skip to content

Commit

Permalink
tx: implement ECDSA signing and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
xiphon committed Mar 31, 2019
1 parent 7edddda commit c40eabf
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 16 deletions.
62 changes: 46 additions & 16 deletions safebox/tx/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ package tx
import (
"bytes"
"crypto/ecdsa"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"io"
"math/big"
"strings"

"github.com/pasl-project/pasl/accounter"
Expand All @@ -43,12 +45,13 @@ const (
txTypeChangekey
)

type commonOperation interface {
type CommonOperation interface {
GetAmount() uint64
GetAccount() uint32
GetDestAccount() uint32
GetFee() uint64
GetPayload() []byte
GetType() txType

Validate(getAccount func(number uint32) *accounter.Account) (context interface{}, err error)
Apply(index uint32, context interface{}, accounter *accounter.Accounter) ([]uint32, error)
Expand All @@ -63,7 +66,7 @@ type commonOperation interface {
// TODO: rename to transaction
type Tx struct {
Type txType
commonOperation
CommonOperation
}

type TxMetadata struct {
Expand All @@ -78,17 +81,47 @@ type OperationsNetwork struct {
Operations []Tx
}

func Sign(tx CommonOperation, priv *ecdsa.PrivateKey) (txID string, raw []byte, err error) {
_, _, public := tx.getSourceInfo()
public.Curve = priv.Curve
public.X = big.NewInt(0).Set(priv.PublicKey.X)
public.Y = big.NewInt(0).Set(priv.PublicKey.Y)

data := tx.getBufferToSign()
r, s, err := ecdsa.Sign(rand.Reader, priv, data)
if err != nil {
return "", nil, err
}

signature := tx.getSignature()
signature.R = r.Bytes()
signature.S = s.Bytes()

transaction := Tx{
Type: tx.GetType(),
CommonOperation: tx,
}
operations := OperationsNetwork{
Operations: []Tx{transaction},
}
serialized := bytes.NewBuffer(nil)
if err := operations.Serialize(serialized); err != nil {
return "", nil, err
}
return transaction.GetTxIdString(), serialized.Bytes(), nil
}

func (this *Tx) GetFee() uint64 {
return this.commonOperation.GetFee()
return this.CommonOperation.GetFee()
}

func (this *Tx) ValidateSignature() error {
_, _, publicKey := this.commonOperation.getSourceInfo()
return checkSignature(publicKey, this.commonOperation.getBufferToSign(), this.commonOperation.getSignature())
_, _, publicKey := this.CommonOperation.getSourceInfo()
return checkSignature(publicKey, this.CommonOperation.getBufferToSign(), this.CommonOperation.getSignature())
}

func (this *Tx) Validate(getAccount func(number uint32) *accounter.Account) (context interface{}, err error) {
number, _, publicKey := this.commonOperation.getSourceInfo()
number, _, publicKey := this.CommonOperation.getSourceInfo()

source := getAccount(number)
if source == nil {
Expand All @@ -98,7 +131,7 @@ func (this *Tx) Validate(getAccount func(number uint32) *accounter.Account) (con
return nil, errors.New("Source account invalid public key")
}

return this.commonOperation.Validate(getAccount)
return this.CommonOperation.Validate(getAccount)
}

func (this *Tx) GetRipemd16Hash() []byte {
Expand All @@ -109,7 +142,7 @@ func (this *Tx) GetRipemd16Hash() []byte {
}
buffer := utils.Serialize(toHash{
ToSign: &utils.BytesWithoutLengthPrefix{
Bytes: this.commonOperation.getBufferToSign(),
Bytes: this.CommonOperation.getBufferToSign(),
},
R: &utils.BytesWithoutLengthPrefix{
Bytes: this.getSignature().R,
Expand All @@ -134,12 +167,12 @@ func (this *Tx) GetTxId() []byte {
OperationId uint32
Hash utils.Serializable
}
source, operationId, _ := this.commonOperation.getSourceInfo()
source, operationID, _ := this.CommonOperation.getSourceInfo()

return utils.Serialize(txId{
Reserved: 0,
Source: source,
OperationId: operationId,
OperationId: operationID,
Hash: &utils.BytesWithoutLengthPrefix{
Bytes: this.GetRipemd16Hash(),
},
Expand Down Expand Up @@ -183,14 +216,11 @@ func TxFromMetadata(metadataSerialized []byte) (*TxMetadata, *Tx, error) {
}

func (this *Tx) Serialize(w io.Writer) error {
if _, err := w.Write(utils.Serialize(&this.Type)); err != nil {
return err
}
return this.SerializeUnderlying(w)
}

func (this *Tx) SerializeUnderlying(w io.Writer) error {
return this.commonOperation.Serialize(w)
return this.CommonOperation.Serialize(w)
}

func (this *Tx) deserializeUnderlying(r io.Reader) error {
Expand All @@ -200,14 +230,14 @@ func (this *Tx) deserializeUnderlying(r io.Reader) error {
if err := utils.Deserialize(&tx, r); err != nil {
return err
}
this.commonOperation = &tx
this.CommonOperation = &tx
return nil
case txTypeChangekey:
var changeKey ChangeKey
if err := utils.Deserialize(&changeKey, r); err != nil {
return err
}
this.commonOperation = &changeKey
this.CommonOperation = &changeKey
return nil
default:
return errors.New("Unknown operation type")
Expand Down
46 changes: 46 additions & 0 deletions safebox/tx/tx_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package tx

import (
"bytes"
"encoding/hex"
"testing"

"github.com/pasl-project/pasl/crypto"
"github.com/pasl-project/pasl/utils"
)

func TestSignature(t *testing.T) {
raw := []byte{}
key, err := crypto.NewKeyByType(crypto.NIDsecp256k1)
if err != nil {
t.Fatal(err)
}

{
serializedTx, _ := hex.DecodeString("020000008fd003000100000000000000000000000000ca022000666293eb108763de780fd6ee5f2d8f92a9c69fc3e36b5a40a9e8d25523f619c7200097fc795b55d50a41dc8abd099adf96152a2f07a1c35480dd4512abc4e42669014600ca02200089927599939edd01c65628e7e25f7a1ff511c9806d75dbf2917131c4217814dd20006cf4bc42292ed111c111d17d1a7b37d36f077340b60e918da2aa424ce2777d8b2000e90d7f237b1ba873103e11eb4f0002d35ee43d3b7c295ec7fe5d0393abe236372000e0491191fc9567fceb4c6c91d4d6e95edecff23f2a45cb60b5bddd613c2d348a")
var tx Tx
if err := utils.Deserialize(&tx, bytes.NewBuffer(serializedTx)); err != nil {
t.Fatal(err)
}
if err := tx.ValidateSignature(); err != nil {
t.Fatal(err)
}
_, raw, err = Sign(&tx, key.Convert())
if err != nil {
t.Fatal(err)
}
}

var operations OperationsNetwork
if err := utils.Deserialize(&operations, bytes.NewBuffer(raw)); err != nil {
t.Fatal(err)
}
tx := operations.Operations[0]
_, _, newPublic := tx.getSourceInfo()
if !newPublic.Equal(key.Public) {
t.FailNow()
}
if err := tx.ValidateSignature(); err != nil {
t.Fatal(err)
}
}
4 changes: 4 additions & 0 deletions safebox/tx/txchangekey.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,7 @@ func (this *ChangeKey) getSignature() *crypto.SignatureSerialized {
func (this *ChangeKey) getSourceInfo() (number uint32, operationId uint32, publicKey *crypto.Public) {
return this.Source, this.OperationId, &this.PublicKey
}

func (this *ChangeKey) GetType() txType {
return txTypeChangekey
}
13 changes: 13 additions & 0 deletions safebox/tx/txchangekey_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,17 @@ func TestTxSignBuffer(t *testing.T) {
if !bytes.Equal(valid, txRipemd160Hash) {
t.Fatalf("%x != %x", valid, txRipemd160Hash)
}

if tx.GetAmount() != 0 {
t.FailNow()
}
if tx.GetDestAccount() != tx.GetAccount() {
t.FailNow()
}
if tx.GetFee() != 0 {
t.FailNow()
}
if !bytes.Equal(tx.GetPayload(), []byte{}) {
t.FailNow()
}
}
4 changes: 4 additions & 0 deletions safebox/tx/txtransfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,7 @@ func (this *Transfer) getSourceInfo() (number uint32, operationId uint32, public
func (this *Transfer) getSignature() *crypto.SignatureSerialized {
return &this.Signature
}

func (this *Transfer) GetType() txType {
return txTypeTransfer
}

0 comments on commit c40eabf

Please sign in to comment.