Module: Mpp::Methods::Tempo::Attribution
- Defined in:
- lib/mpp/methods/tempo/attribution.rb
Defined Under Namespace
Classes: DecodedMemo
Constant Summary collapse
- VERSION =
0x01- ANONYMOUS =
"\x00" * 10
Class Method Summary collapse
-
.decode(memo) ⇒ Object
Decode an MPP attribution memo.
-
.encode(server_id:, client_id: nil, challenge_id: nil) ⇒ Object
Encode an MPP attribution memo (32 bytes).
- .fingerprint(value) ⇒ Object
-
.keccak256(data) ⇒ Object
Compute keccak256 hash.
-
.mpp_memo?(memo) ⇒ Boolean
Check if a memo is an MPP attribution memo.
-
.tag ⇒ Object
Compute TAG = keccak256(“mpp”).
-
.verify_server(memo, server_id) ⇒ Object
Verify server fingerprint in memo.
Class Method Details
.decode(memo) ⇒ Object
Decode an MPP attribution memo.
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/mpp/methods/tempo/attribution.rb', line 87 def decode(memo) return nil unless mpp_memo?(memo) begin version = memo[10, 2].to_i(16) server_fingerprint = "0x#{memo[12, 20]}" client_hex = memo[32, 20] nonce = "0x#{memo[52..]}" client_bytes = [client_hex].pack("H*") client_fingerprint = (client_bytes == ANONYMOUS.b) ? nil : "0x#{client_hex}" rescue ArgumentError return nil end DecodedMemo.new( version: version, server_fingerprint: server_fingerprint, client_fingerprint: client_fingerprint, nonce: nonce ) end |
.encode(server_id:, client_id: nil, challenge_id: nil) ⇒ Object
Encode an MPP attribution memo (32 bytes).
Byte Layout:
0..3: TAG = keccak256("mpp")[0:4]
4: version (0x01)
5..14: serverId fingerprint
15..24: clientId fingerprint or zeros
25..31: random nonce
44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/mpp/methods/tempo/attribution.rb', line 44 def encode(server_id:, client_id: nil, challenge_id: nil) buf = "\x00".b * 32 buf[0, 4] = tag buf[4] = [VERSION].pack("C") buf[5, 10] = fingerprint(server_id) buf[15, 10] = client_id ? fingerprint(client_id) : ANONYMOUS.b buf[25, 7] = if challenge_id keccak256(challenge_id.encode(Encoding::UTF_8))[0, 7] else SecureRandom.random_bytes(7) end "0x#{buf.unpack1("H*")}" end |
.fingerprint(value) ⇒ Object
32 33 34 |
# File 'lib/mpp/methods/tempo/attribution.rb', line 32 def fingerprint(value) keccak256(value.encode(Encoding::UTF_8))[0, 10] end |
.keccak256(data) ⇒ Object
Compute keccak256 hash. Uses OpenSSL if available, otherwise pure Ruby.
17 18 19 20 21 22 23 24 25 |
# File 'lib/mpp/methods/tempo/attribution.rb', line 17 def keccak256(data) # Try eth gem's keccak first Kernel.require "eth" Eth::Util.keccak256(data) rescue LoadError # Fallback: use OpenSSL's SHA3-256 (not exactly keccak, but close) # For production, the eth gem should be installed OpenSSL::Digest.new("SHA3-256").digest(data) end |
.mpp_memo?(memo) ⇒ Boolean
Check if a memo is an MPP attribution memo.
59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/mpp/methods/tempo/attribution.rb', line 59 def mpp_memo?(memo) return false unless memo.length == 66 begin memo_tag = [memo[2, 8]].pack("H*") memo_version = memo[10, 2].to_i(16) rescue ArgumentError return false end memo_tag == tag && memo_version == VERSION end |
.tag ⇒ Object
Compute TAG = keccak256(“mpp”)
28 29 30 |
# File 'lib/mpp/methods/tempo/attribution.rb', line 28 def tag @tag ||= keccak256("mpp".b)[0, 4] end |
.verify_server(memo, server_id) ⇒ Object
Verify server fingerprint in memo.
72 73 74 75 76 77 78 79 80 81 |
# File 'lib/mpp/methods/tempo/attribution.rb', line 72 def verify_server(memo, server_id) return false unless mpp_memo?(memo) begin memo_server = [memo[12, 20]].pack("H*") rescue ArgumentError return false end memo_server == fingerprint(server_id) end |