114 lines
3.1 KiB
Go
114 lines
3.1 KiB
Go
package commands
|
|
|
|
import (
|
|
"github.com/pkg/errors"
|
|
bc "github.com/tendermint/basecoin/types"
|
|
crypto "github.com/tendermint/go-crypto"
|
|
keys "github.com/tendermint/go-crypto/keys"
|
|
wire "github.com/tendermint/go-wire"
|
|
)
|
|
|
|
type SendTx struct {
|
|
chainID string
|
|
signers []crypto.PubKey
|
|
Tx *bc.SendTx
|
|
}
|
|
|
|
var _ keys.Signable = &SendTx{}
|
|
|
|
// SignBytes returned the unsigned bytes, needing a signature
|
|
func (s *SendTx) SignBytes() []byte {
|
|
return s.Tx.SignBytes(s.chainID)
|
|
}
|
|
|
|
// Sign will add a signature and pubkey.
|
|
//
|
|
// Depending on the Signable, one may be able to call this multiple times for multisig
|
|
// Returns error if called with invalid data or too many times
|
|
func (s *SendTx) Sign(pubkey crypto.PubKey, sig crypto.Signature) error {
|
|
addr := pubkey.Address()
|
|
set := s.Tx.SetSignature(addr, sig)
|
|
if !set {
|
|
return errors.Errorf("Cannot add signature for address %X", addr)
|
|
}
|
|
s.signers = append(s.signers, pubkey)
|
|
return nil
|
|
}
|
|
|
|
// Signers will return the public key(s) that signed if the signature
|
|
// is valid, or an error if there is any issue with the signature,
|
|
// including if there are no signatures
|
|
func (s *SendTx) Signers() ([]crypto.PubKey, error) {
|
|
if len(s.signers) == 0 {
|
|
return nil, errors.New("No signatures on SendTx")
|
|
}
|
|
return s.signers, nil
|
|
}
|
|
|
|
// TxBytes returns the transaction data as well as all signatures
|
|
// It should return an error if Sign was never called
|
|
func (s *SendTx) TxBytes() ([]byte, error) {
|
|
// TODO: verify it is signed
|
|
|
|
// Code and comment from: basecoin/cmd/basecoin/commands/tx.go
|
|
// Don't you hate having to do this?
|
|
// How many times have I lost an hour over this trick?!
|
|
txBytes := wire.BinaryBytes(struct {
|
|
bc.Tx `json:"unwrap"`
|
|
}{s.Tx})
|
|
return txBytes, nil
|
|
}
|
|
|
|
// AddSigner sets address and pubkey info on the tx based on the key that
|
|
// will be used for signing
|
|
func (s *SendTx) AddSigner(pk crypto.PubKey) {
|
|
// get addr if available
|
|
var addr []byte
|
|
if !pk.Empty() {
|
|
addr = pk.Address()
|
|
}
|
|
|
|
// set the send address, and pubkey if needed
|
|
in := s.Tx.Inputs
|
|
in[0].Address = addr
|
|
if in[0].Sequence == 1 {
|
|
in[0].PubKey = pk
|
|
}
|
|
}
|
|
|
|
// TODO: this should really be in the basecoin.types SendTx,
|
|
// but that code is too ugly now, needs refactor..
|
|
func (s *SendTx) ValidateBasic() error {
|
|
if s.chainID == "" {
|
|
return errors.New("No chain-id specified")
|
|
}
|
|
for _, in := range s.Tx.Inputs {
|
|
if len(in.Address) != 20 {
|
|
return errors.Errorf("Invalid input address length: %d", len(in.Address))
|
|
}
|
|
if !in.Coins.IsValid() {
|
|
return errors.Errorf("Invalid input coins %v", in.Coins)
|
|
}
|
|
if in.Coins.IsZero() {
|
|
return errors.New("Input coins cannot be zero")
|
|
}
|
|
if in.Sequence <= 0 {
|
|
return errors.New("Sequence must be greater than 0")
|
|
}
|
|
}
|
|
for _, out := range s.Tx.Outputs {
|
|
// we now allow chain/addr, so it can be more than 20 bytes
|
|
if len(out.Address) < 20 {
|
|
return errors.Errorf("Invalid output address length: %d", len(out.Address))
|
|
}
|
|
if !out.Coins.IsValid() {
|
|
return errors.Errorf("Invalid output coins %v", out.Coins)
|
|
}
|
|
if out.Coins.IsZero() {
|
|
return errors.New("Output coins cannot be zero")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|