Class: CITA::TransactionSigner

Inherits:
Object
  • Object
show all
Defined in:
lib/cita/transaction_signer.rb

Class Method Summary collapse

Class Method Details

.decode(tx_content, recover: true) ⇒ Hash

decode and parse bytes to hex string

Parameters:

  • tx_content (String)

    hex string

  • recover (Boolean) (defaults to: true)

    set to false if you don’t want to recover from address, default is true

Returns:

  • (Hash)


120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/cita/transaction_signer.rb', line 120

def decode(tx_content, recover: true)
  data = original_decode(tx_content, recover: recover)
  utx = data[:unverified_transaction]
  tx = utx[:transaction]
  version = tx[:version]

  tx[:value] = Utils.from_bytes(tx[:value])
  tx[:data] = Utils.from_bytes(tx[:data])
  tx[:nonce] = Utils.add_prefix_for_not_blank(tx[:nonce])
  utx[:signature] = Utils.from_bytes(utx[:signature])

  if version == 0 # rubocop:disable Style/NumericPredicate
    tx.delete(:to_v1)
    tx.delete(:chain_id_v1)
    tx[:to] = Utils.add_prefix_for_not_blank(tx[:to])
  elsif [1, 2].include? version
    tx[:to] = Utils.from_bytes(tx[:to])
    tx[:chain_id] = Utils.from_bytes(tx[:chain_id])
  end

  data
end

.encode(transaction, private_key) ⇒ Object

sign transaction

Parameters:



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/cita/transaction_signer.rb', line 13

def encode(transaction, private_key)
  tx = CITA::Protos::Transaction.new

  to = CITA::Utils.remove_hex_prefix(transaction.to)&.downcase

  tx.nonce = transaction.nonce
  tx.quota = transaction.quota
  tx.data = CITA::Utils.to_bytes(transaction.data)
  tx.value = hex_to_bytes(transaction.value)
  tx.version = transaction.version
  tx.valid_until_block = transaction.valid_until_block

  if transaction.version.zero?
    tx.to = to unless to.nil?
    tx.chain_id = transaction.chain_id
  elsif transaction.version == 1 || transaction.version == 2
    tx.to_v1 = Utils.to_bytes(to) unless to.nil?
    tx.chain_id_v1 = hex_to_bytes(transaction.chain_id)
  end

  encoded_tx = Protos::Transaction.encode(tx)

  private_key_bytes = CITA::Utils.to_bytes(private_key)

  protobuf_hash = Utils.keccak256(encoded_tx)

  signature = Ciri::Crypto.ecdsa_signature(private_key_bytes, protobuf_hash).signature

  unverified_tx = Protos::UnverifiedTransaction.new(transaction: tx, signature: signature)

  encoded_unverified_tx = Protos::UnverifiedTransaction.encode(unverified_tx)

  CITA::Utils.from_bytes(encoded_unverified_tx)
end

.original_decode(tx_content, recover: true) ⇒ Hash

decode and support forks

Parameters:

  • tx_content (String)

    hex string

  • recover (Boolean) (defaults to: true)

    set to false if you don’t want to recover from address, default is true

Returns:

  • (Hash)


96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/cita/transaction_signer.rb', line 96

def original_decode(tx_content, recover: true)
  data = simple_decode(tx_content, recover: recover)
  utx = data[:unverified_transaction]
  tx = utx[:transaction]
  version = tx[:version]

  if version == 0 # rubocop:disable Style/NumericPredicate
    tx.delete(:to_v1)
    tx.delete(:chain_id_v1)
  elsif [1, 2].include? version
    tx[:to] = tx.delete(:to_v1)
    tx[:chain_id] = tx.delete(:chain_id_v1)
  else
    raise Transaction::VersionError, "transaction version error, expected 0, 1 or 2, got #{version}"
  end

  data
end

.sender_info(unverified_transaction) ⇒ Hash

get sender info (from address & public key)

Parameters:

  • unverified_transaction (Hash)

Returns:

  • (Hash)

    address & public_key



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/cita/transaction_signer.rb', line 52

def sender_info(unverified_transaction)
  signature = unverified_transaction["signature"]

  transaction = unverified_transaction["transaction"]
  msg = Protos::Transaction.encode(transaction)
  msg_hash = CITA::Utils.keccak256(msg)
  pubkey = Ciri::Crypto.ecdsa_recover(msg_hash, signature)
  pubkey_hex = Utils.from_bytes(pubkey[1..-1])

  from_address = Utils.keccak256(pubkey[1..-1])[-20..-1]
  from_address_hex = Utils.from_bytes(from_address)

  {
    address: from_address_hex,
    public_key: pubkey_hex
  }
end

.simple_decode(tx_content, recover: true) ⇒ Object

decode transaction, CITA v0.21 returns the ‘from` address in `getTransaction` RPC call so you can not to recover `from` address from context

Parameters:

  • tx_content (String)

    hex string

  • recover (Boolean) (defaults to: true)

    set to false if you don’t want to recover from address, default is true



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/cita/transaction_signer.rb', line 75

def simple_decode(tx_content, recover: true)
  content_bytes = CITA::Utils.to_bytes(tx_content)
  unverified_transaction = Protos::UnverifiedTransaction.decode(content_bytes)

  info = {
    unverified_transaction: unverified_transaction.to_h
  }

  if recover
    sender = sender_info(unverified_transaction)
    info[:sender] = sender
  end

  info
end