You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

331 lines
11 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. package types
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "io"
  6. "github.com/tendermint/tendermint/account"
  7. "github.com/tendermint/tendermint/binary"
  8. . "github.com/tendermint/tendermint/common"
  9. )
  10. var (
  11. ErrTxInvalidAddress = errors.New("Error invalid address")
  12. ErrTxDuplicateAddress = errors.New("Error duplicate address")
  13. ErrTxInvalidAmount = errors.New("Error invalid amount")
  14. ErrTxInsufficientFunds = errors.New("Error insufficient funds")
  15. ErrTxInsufficientGasPrice = errors.New("Error insufficient gas price")
  16. ErrTxUnknownPubKey = errors.New("Error unknown pubkey")
  17. ErrTxInvalidPubKey = errors.New("Error invalid pubkey")
  18. ErrTxInvalidSignature = errors.New("Error invalid signature")
  19. ErrTxInvalidString = errors.New("Error invalid string")
  20. ErrIncorrectOwner = errors.New("Error incorrect owner")
  21. )
  22. type ErrTxInvalidSequence struct {
  23. Got int
  24. Expected int
  25. }
  26. func (e ErrTxInvalidSequence) Error() string {
  27. return Fmt("Error invalid sequence. Got %d, expected %d", e.Got, e.Expected)
  28. }
  29. /*
  30. Tx (Transaction) is an atomic operation on the ledger state.
  31. Account Txs:
  32. - SendTx Send coins to address
  33. - CallTx Send a msg to a contract that runs in the vm
  34. - NameTx Store some value under a name in the global namereg
  35. Validation Txs:
  36. - BondTx New validator posts a bond
  37. - UnbondTx Validator leaves
  38. - DupeoutTx Validator dupes out (equivocates)
  39. */
  40. type Tx interface {
  41. WriteSignBytes(chainID string, w io.Writer, n *int64, err *error)
  42. }
  43. // Types of Tx implementations
  44. const (
  45. // Account transactions
  46. TxTypeSend = byte(0x01)
  47. TxTypeCall = byte(0x02)
  48. TxTypeName = byte(0x03)
  49. // Validation transactions
  50. TxTypeBond = byte(0x11)
  51. TxTypeUnbond = byte(0x12)
  52. TxTypeRebond = byte(0x13)
  53. TxTypeDupeout = byte(0x14)
  54. )
  55. // for binary.readReflect
  56. var _ = binary.RegisterInterface(
  57. struct{ Tx }{},
  58. binary.ConcreteType{&SendTx{}, TxTypeSend},
  59. binary.ConcreteType{&CallTx{}, TxTypeCall},
  60. binary.ConcreteType{&NameTx{}, TxTypeName},
  61. binary.ConcreteType{&BondTx{}, TxTypeBond},
  62. binary.ConcreteType{&UnbondTx{}, TxTypeUnbond},
  63. binary.ConcreteType{&RebondTx{}, TxTypeRebond},
  64. binary.ConcreteType{&DupeoutTx{}, TxTypeDupeout},
  65. )
  66. //-----------------------------------------------------------------------------
  67. type TxInput struct {
  68. Address []byte `json:"address"` // Hash of the PubKey
  69. Amount int64 `json:"amount"` // Must not exceed account balance
  70. Sequence int `json:"sequence"` // Must be 1 greater than the last committed TxInput
  71. Signature account.Signature `json:"signature"` // Depends on the PubKey type and the whole Tx
  72. PubKey account.PubKey `json:"pub_key"` // Must not be nil, may be nil
  73. }
  74. func (txIn *TxInput) ValidateBasic() error {
  75. if len(txIn.Address) != 20 {
  76. return ErrTxInvalidAddress
  77. }
  78. if txIn.Amount == 0 {
  79. return ErrTxInvalidAmount
  80. }
  81. return nil
  82. }
  83. func (txIn *TxInput) WriteSignBytes(w io.Writer, n *int64, err *error) {
  84. binary.WriteTo([]byte(Fmt(`{"address":"%X","amount":%v,"sequence":%v}`, txIn.Address, txIn.Amount, txIn.Sequence)), w, n, err)
  85. }
  86. func (txIn *TxInput) String() string {
  87. return Fmt("TxInput{%X,%v,%v,%v,%v}", txIn.Address, txIn.Amount, txIn.Sequence, txIn.Signature, txIn.PubKey)
  88. }
  89. //-----------------------------------------------------------------------------
  90. type TxOutput struct {
  91. Address []byte `json:"address"` // Hash of the PubKey
  92. Amount int64 `json:"amount"` // The sum of all outputs must not exceed the inputs.
  93. }
  94. func (txOut *TxOutput) ValidateBasic() error {
  95. if len(txOut.Address) != 20 {
  96. return ErrTxInvalidAddress
  97. }
  98. if txOut.Amount == 0 {
  99. return ErrTxInvalidAmount
  100. }
  101. return nil
  102. }
  103. func (txOut *TxOutput) WriteSignBytes(w io.Writer, n *int64, err *error) {
  104. binary.WriteTo([]byte(Fmt(`{"address":"%X","amount":%v}`, txOut.Address, txOut.Amount)), w, n, err)
  105. }
  106. func (txOut *TxOutput) String() string {
  107. return Fmt("TxOutput{%X,%v}", txOut.Address, txOut.Amount)
  108. }
  109. //-----------------------------------------------------------------------------
  110. type SendTx struct {
  111. Inputs []*TxInput `json:"inputs"`
  112. Outputs []*TxOutput `json:"outputs"`
  113. }
  114. func (tx *SendTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
  115. binary.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
  116. binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"inputs":[`, TxTypeSend)), w, n, err)
  117. for i, in := range tx.Inputs {
  118. in.WriteSignBytes(w, n, err)
  119. if i != len(tx.Inputs)-1 {
  120. binary.WriteTo([]byte(","), w, n, err)
  121. }
  122. }
  123. binary.WriteTo([]byte(`],"outputs":[`), w, n, err)
  124. for i, out := range tx.Outputs {
  125. out.WriteSignBytes(w, n, err)
  126. if i != len(tx.Outputs)-1 {
  127. binary.WriteTo([]byte(","), w, n, err)
  128. }
  129. }
  130. binary.WriteTo([]byte(`]}]}`), w, n, err)
  131. }
  132. func (tx *SendTx) String() string {
  133. return Fmt("SendTx{%v -> %v}", tx.Inputs, tx.Outputs)
  134. }
  135. //-----------------------------------------------------------------------------
  136. type CallTx struct {
  137. Input *TxInput `json:"input"`
  138. Address []byte `json:"address"`
  139. GasLimit int64 `json:"gas_limit"`
  140. Fee int64 `json:"fee"`
  141. Data []byte `json:"data"`
  142. }
  143. func (tx *CallTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
  144. binary.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
  145. binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"address":"%X","data":"%X"`, TxTypeCall, tx.Address, tx.Data)), w, n, err)
  146. binary.WriteTo([]byte(Fmt(`,"fee":%v,"gas_limit":%v,"input":`, tx.Fee, tx.GasLimit)), w, n, err)
  147. tx.Input.WriteSignBytes(w, n, err)
  148. binary.WriteTo([]byte(`}]}`), w, n, err)
  149. }
  150. func (tx *CallTx) String() string {
  151. return Fmt("CallTx{%v -> %x: %x}", tx.Input, tx.Address, tx.Data)
  152. }
  153. //-----------------------------------------------------------------------------
  154. type NameTx struct {
  155. Input *TxInput `json:"input"`
  156. Name string `json:"name"`
  157. Data string `json:"data"`
  158. Fee int64 `json:"fee"`
  159. }
  160. func (tx *NameTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
  161. binary.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
  162. binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"data":%s,"fee":%v`, TxTypeName, jsonEscape(tx.Data), tx.Fee)), w, n, err)
  163. binary.WriteTo([]byte(`,"input":`), w, n, err)
  164. tx.Input.WriteSignBytes(w, n, err)
  165. binary.WriteTo([]byte(Fmt(`,"name":%s`, jsonEscape(tx.Name))), w, n, err)
  166. binary.WriteTo([]byte(`}]}`), w, n, err)
  167. }
  168. func (tx *NameTx) ValidateStrings() error {
  169. if len(tx.Name) == 0 {
  170. return errors.New("Name must not be empty")
  171. }
  172. if len(tx.Name) > MaxNameLength {
  173. return errors.New(Fmt("Name is too long. Max %d bytes", MaxNameLength))
  174. }
  175. if len(tx.Data) > MaxDataLength {
  176. return errors.New(Fmt("Data is too long. Max %d bytes", MaxDataLength))
  177. }
  178. if !validateNameRegEntryName(tx.Name) {
  179. return errors.New(Fmt("Invalid characters found in NameTx.Name (%s). Only alphanumeric, underscores, and forward slashes allowed", tx.Name))
  180. }
  181. if !validateNameRegEntryData(tx.Data) {
  182. return errors.New(Fmt("Invalid characters found in NameTx.Data (%s). Only the kind of things found in a JSON file are allowed", tx.Data))
  183. }
  184. return nil
  185. }
  186. func (tx *NameTx) BaseEntryCost() int64 {
  187. return BaseEntryCost(tx.Name, tx.Data)
  188. }
  189. func (tx *NameTx) String() string {
  190. return Fmt("NameTx{%v -> %s: %s}", tx.Input, tx.Name, tx.Data)
  191. }
  192. //-----------------------------------------------------------------------------
  193. type BondTx struct {
  194. PubKey account.PubKeyEd25519 `json:"pub_key"`
  195. Signature account.SignatureEd25519 `json:"signature"`
  196. Inputs []*TxInput `json:"inputs"`
  197. UnbondTo []*TxOutput `json:"unbond_to"`
  198. }
  199. func (tx *BondTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
  200. binary.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
  201. binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"inputs":[`, TxTypeBond)), w, n, err)
  202. for i, in := range tx.Inputs {
  203. in.WriteSignBytes(w, n, err)
  204. if i != len(tx.Inputs)-1 {
  205. binary.WriteTo([]byte(","), w, n, err)
  206. }
  207. }
  208. binary.WriteTo([]byte(Fmt(`],"pub_key":`)), w, n, err)
  209. binary.WriteTo(binary.JSONBytes(tx.PubKey), w, n, err)
  210. binary.WriteTo([]byte(`,"unbond_to":[`), w, n, err)
  211. for i, out := range tx.UnbondTo {
  212. out.WriteSignBytes(w, n, err)
  213. if i != len(tx.UnbondTo)-1 {
  214. binary.WriteTo([]byte(","), w, n, err)
  215. }
  216. }
  217. binary.WriteTo([]byte(`]}]}`), w, n, err)
  218. }
  219. func (tx *BondTx) String() string {
  220. return Fmt("BondTx{%v: %v -> %v}", tx.PubKey, tx.Inputs, tx.UnbondTo)
  221. }
  222. //-----------------------------------------------------------------------------
  223. type UnbondTx struct {
  224. Address []byte `json:"address"`
  225. Height int `json:"height"`
  226. Signature account.SignatureEd25519 `json:"signature"`
  227. }
  228. func (tx *UnbondTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
  229. binary.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
  230. binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"address":"%X","height":%v}]}`, TxTypeUnbond, tx.Address, tx.Height)), w, n, err)
  231. }
  232. func (tx *UnbondTx) String() string {
  233. return Fmt("UnbondTx{%X,%v,%v}", tx.Address, tx.Height, tx.Signature)
  234. }
  235. //-----------------------------------------------------------------------------
  236. type RebondTx struct {
  237. Address []byte `json:"address"`
  238. Height int `json:"height"`
  239. Signature account.SignatureEd25519 `json:"signature"`
  240. }
  241. func (tx *RebondTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
  242. binary.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
  243. binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"address":"%X","height":%v}]}`, TxTypeRebond, tx.Address, tx.Height)), w, n, err)
  244. }
  245. func (tx *RebondTx) String() string {
  246. return Fmt("RebondTx{%X,%v,%v}", tx.Address, tx.Height, tx.Signature)
  247. }
  248. //-----------------------------------------------------------------------------
  249. type DupeoutTx struct {
  250. Address []byte `json:"address"`
  251. VoteA Vote `json:"vote_a"`
  252. VoteB Vote `json:"vote_b"`
  253. }
  254. func (tx *DupeoutTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
  255. panic("DupeoutTx has no sign bytes")
  256. }
  257. func (tx *DupeoutTx) String() string {
  258. return Fmt("DupeoutTx{%X,%v,%v}", tx.Address, tx.VoteA, tx.VoteB)
  259. }
  260. //-----------------------------------------------------------------------------
  261. func TxId(chainID string, tx Tx) []byte {
  262. signBytes := account.SignBytes(chainID, tx)
  263. return binary.BinaryRipemd160(signBytes)
  264. }
  265. //--------------------------------------------------------------------------------
  266. // Contract: This function is deterministic and completely reversible.
  267. func jsonEscape(str string) string {
  268. escapedBytes, err := json.Marshal(str)
  269. if err != nil {
  270. panic(Fmt("Error json-escaping a string", str))
  271. }
  272. return string(escapedBytes)
  273. }