Просмотр исходного кода

Rework token lists and ID generation.

parazyd 4 лет назад
Родитель
Сommit
609afbd9c7

+ 2 - 0
Cargo.lock

@@ -1261,6 +1261,7 @@ dependencies = [
  "async-std",
  "async-trait",
  "blake3",
+ "bs58",
  "chrono",
  "ctrlc-async",
  "darkfi",
@@ -1270,6 +1271,7 @@ dependencies = [
  "lazy-init",
  "log",
  "num-bigint",
+ "pasta_curves",
  "rand",
  "serde",
  "serde_derive",

+ 2 - 0
bin/darkfid2/Cargo.toml

@@ -14,6 +14,7 @@ async-executor = "1.4.1"
 async-std = "1.11.0"
 async-trait = "0.1.53"
 blake3 = "1.3.1"
+bs58 = "0.4.0"
 chrono = "0.4.19"
 ctrlc-async = {version = "3.2.2", default-features = false, features = ["async-std", "termination"]}
 darkfi = {path = "../../", features = ["blockchain", "wallet", "rpc", "net", "node"]}
@@ -23,6 +24,7 @@ fxhash = "0.2.1"
 lazy-init = "0.5.0"
 log = "0.4.16"
 num-bigint = {version = "0.4.3", features = ["serde"]}
+pasta_curves = "0.3.1"
 rand = "0.8.5"
 serde_json = "1.0.79"
 simplelog = "0.12.0"

+ 24 - 26
bin/darkfid2/src/main.rs

@@ -24,11 +24,7 @@ use darkfi::{
         util::Timestamp,
         ValidatorState, MAINNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_HASH_BYTES,
     },
-    crypto::{
-        address::Address,
-        keypair::PublicKey,
-        token_list::{DrkTokenList, TokenList},
-    },
+    crypto::{address::Address, keypair::PublicKey, token_list::DrkTokenList},
     net,
     net::P2pPtr,
     node::Client,
@@ -150,10 +146,7 @@ pub struct Darkfid {
     sync_p2p: Option<P2pPtr>,
     client: Arc<Client>,
     validator_state: ValidatorStatePtr,
-    drk_tokenlist: DrkTokenList,
-    btc_tokenlist: TokenList,
-    eth_tokenlist: TokenList,
-    sol_tokenlist: TokenList,
+    tokenlist: DrkTokenList,
 }
 
 // JSON-RPC methods
@@ -193,17 +186,8 @@ impl Darkfid {
         validator_state: ValidatorStatePtr,
         consensus_p2p: Option<P2pPtr>,
         sync_p2p: Option<P2pPtr>,
+        tokenlist: DrkTokenList,
     ) -> Result<Self> {
-        debug!("Parsing token lists...");
-        let btc_tokenlist =
-            TokenList::new(include_bytes!("../../../contrib/token/bitcoin_token_list.min.json"))?;
-        let eth_tokenlist =
-            TokenList::new(include_bytes!("../../../contrib/token/erc20_token_list.min.json"))?;
-        let sol_tokenlist =
-            TokenList::new(include_bytes!("../../../contrib/token/solana_token_list.min.json"))?;
-        debug!("Creating drk tokenlist");
-        let drk_tokenlist = DrkTokenList::new(&sol_tokenlist, &eth_tokenlist, &btc_tokenlist)?;
-
         debug!("Waiting for validator state lock");
         let client = validator_state.read().await.client.clone();
         debug!("Released validator state lock");
@@ -214,10 +198,7 @@ impl Darkfid {
             sync_p2p,
             client,
             validator_state,
-            drk_tokenlist,
-            btc_tokenlist,
-            eth_tokenlist,
-            sol_tokenlist,
+            tokenlist,
         })
     }
 }
@@ -283,6 +264,15 @@ async fn realmain(args: Args, ex: Arc<Executor<'_>>) -> Result<()> {
     )
     .await?;
 
+    debug!("Parsing token lists...");
+    let tokenlist = DrkTokenList::new(&[
+        ("drk", include_bytes!("../../../contrib/token/darkfi_token_list.min.json")),
+        ("btc", include_bytes!("../../../contrib/token/bitcoin_token_list.min.json")),
+        ("eth", include_bytes!("../../../contrib/token/erc20_token_list.min.json")),
+        ("sol", include_bytes!("../../../contrib/token/solana_token_list.min.json")),
+    ])?;
+    debug!("Finished parsing token lists");
+
     let sync_p2p = {
         info!("Registering block sync P2P protocols...");
         let sync_network_settings = net::Settings {
@@ -298,10 +288,16 @@ async fn realmain(args: Args, ex: Arc<Executor<'_>>) -> Result<()> {
         let registry = p2p.protocol_registry();
 
         let _state = state.clone();
+        let _tokenlist = tokenlist.clone();
         registry
             .register(net::SESSION_ALL, move |channel, p2p| {
                 let state = _state.clone();
-                async move { ProtocolSync::init(channel, state, p2p, args.consensus).await.unwrap() }
+                let tokenlist = _tokenlist.clone();
+                async move {
+                    ProtocolSync::init(channel, state, tokenlist, p2p, args.consensus)
+                        .await
+                        .unwrap()
+                }
             })
             .await;
 
@@ -376,7 +372,9 @@ async fn realmain(args: Args, ex: Arc<Executor<'_>>) -> Result<()> {
     };
 
     // Initialize program state
-    let darkfid = Darkfid::new(state.clone(), consensus_p2p.clone(), sync_p2p.clone()).await?;
+    let darkfid =
+        Darkfid::new(state.clone(), consensus_p2p.clone(), sync_p2p.clone(), tokenlist.clone())
+            .await?;
     let darkfid = Arc::new(darkfid);
 
     // JSON-RPC server
@@ -394,7 +392,7 @@ async fn realmain(args: Args, ex: Arc<Executor<'_>>) -> Result<()> {
     })
     .detach();
 
-    match block_sync_task(sync_p2p.clone().unwrap(), state.clone()).await {
+    match block_sync_task(sync_p2p.clone().unwrap(), state.clone(), &tokenlist).await {
         Ok(()) => *darkfid.synced.lock().await = true,
         Err(e) => error!("Failed syncing blockchain: {}", e),
     }

+ 12 - 10
bin/darkfid2/src/rpc_tx.rs

@@ -5,14 +5,11 @@ use serde_json::{json, Value};
 
 use darkfi::{
     consensus::Tx,
-    crypto::{address::Address, keypair::PublicKey},
+    crypto::{address::Address, keypair::PublicKey, token_id::generate_id},
     rpc::{
         jsonrpc,
         jsonrpc::{
-            ErrorCode::{
-                InternalError, InvalidAddressParam, InvalidAmountParam, InvalidParams,
-                InvalidTokenIdParam,
-            },
+            ErrorCode::{InternalError, InvalidAddressParam, InvalidAmountParam, InvalidParams},
             JsonResult,
         },
     },
@@ -88,12 +85,17 @@ impl Darkfid {
             }
         };
 
-        let token_id = if let Some(token_id) =
-            self.drk_tokenlist.tokens[&network].get(&token.to_uppercase())
+        let token_id = if let Some(tok) = self.tokenlist.by_net[&network].get(token.to_uppercase())
         {
-            token_id
+            tok.drk_address
         } else {
-            return jsonrpc::error(InvalidTokenIdParam, None, id).into()
+            match generate_id(&network, &token) {
+                Ok(v) => v,
+                Err(e) => {
+                    error!("transfer(): Failed generate_id(): {}", e);
+                    return jsonrpc::error(InternalError, None, id).into()
+                }
+            }
         };
 
         let tx = match self
@@ -101,7 +103,7 @@ impl Darkfid {
             .build_transaction(
                 pubkey,
                 amount,
-                *token_id,
+                token_id,
                 false,
                 self.validator_state.read().await.state_machine.clone(),
             )

+ 47 - 32
bin/darkfid2/src/rpc_wallet.rs

@@ -1,6 +1,7 @@
 use fxhash::FxHashMap;
-use log::error;
+use log::{error, warn};
 use num_bigint::BigUint;
+use pasta_curves::group::ff::PrimeField;
 use serde_json::{json, Value};
 
 use darkfi::{
@@ -15,8 +16,7 @@ use darkfi::{
             JsonResult,
         },
     },
-    util::{decode_base10, encode_base10},
-    Result,
+    util::{decode_base10, encode_base10, NetworkName},
 };
 
 use super::Darkfid;
@@ -199,45 +199,60 @@ impl Darkfid {
     // --> {"jsonrpc": "2.0", "method": "wallet.get_balances", "params": [], "id": 1}
     // <-- {"jsonrpc": "2.0", "result": [{"btc": [100, "Bitcoin"]}, {...}], "id": 1}
     pub async fn get_balances(&self, id: Value, _params: &[Value]) -> JsonResult {
-        let result: Result<FxHashMap<String, (String, String)>> = async {
-            let balances = self.client.get_balances().await?;
-            let mut symbols: FxHashMap<String, (String, String)> = FxHashMap::default();
+        let balances = match self.client.get_balances().await {
+            Ok(v) => v,
+            Err(e) => {
+                error!("Failed fetching balances from wallet: {}", e);
+                return jsonrpc::error(InternalError, None, id).into()
+            }
+        };
 
-            for b in balances.list.iter() {
-                let network: String;
-                let symbol: String;
+        // k: ticker/drk_addr, v: (amount, network, net_addr, drk_addr)
+        let mut ret: FxHashMap<String, (String, String, String, String)> = FxHashMap::default();
 
-                let mut amount = BigUint::from(b.value);
+        for balance in balances.list {
+            let drk_addr = bs58::encode(balance.token_id.to_repr()).into_string();
+            let mut amount = BigUint::from(balance.value);
 
-                if let Some((net, sym)) = self.drk_tokenlist.symbol_from_id(&b.token_id)? {
-                    network = net.to_string();
-                    symbol = sym;
+            let (net_name, net_addr) =
+                if let Some((net, tok)) = self.tokenlist.by_addr.get(&drk_addr) {
+                    (net, tok.net_address.clone())
                 } else {
-                    // TODO: SQL needs to have the mint address for show, not the internal hash.
-                    // TODO: SQL needs to have the network name
-                    network = String::from("UNKNOWN");
-                    symbol = format!("{:?}", b.token_id);
-                }
-
-                if let Some(prev) = symbols.get(&symbol) {
-                    let prev_amnt = decode_base10(&prev.0, 8, true)?;
-                    amount += prev_amnt;
+                    warn!("Could not find network name and token info for {}", drk_addr);
+                    (&NetworkName::DarkFi, "unknown".to_string())
+                };
+
+            let mut ticker = None;
+            for (k, v) in self.tokenlist.by_net[net_name].0.iter() {
+                if v.net_address == net_addr {
+                    ticker = Some(k.clone());
+                    break
                 }
+            }
 
-                let amount = encode_base10(amount, 8);
-                symbols.insert(symbol, (amount, network));
+            if ticker.is_none() {
+                ticker = Some(drk_addr.clone())
             }
 
-            Ok(symbols)
-        }
-        .await;
+            let ticker = ticker.unwrap();
 
-        match result {
-            Ok(res) => jsonrpc::response(json!(res), id).into(),
-            Err(e) => {
-                error!("Failed fetching balances from wallet: {}", e);
-                jsonrpc::error(InternalError, None, id).into()
+            if let Some(prev) = ret.get(&ticker) {
+                // TODO: We shouldn't be hardcoding everything to 8 decimals.
+                let prev_amnt = match decode_base10(&prev.0, 8, false) {
+                    Ok(v) => v,
+                    Err(e) => {
+                        error!("Failed to decode_base10(): {}", e);
+                        return jsonrpc::error(InternalError, None, id).into()
+                    }
+                };
+
+                amount += prev_amnt;
             }
+
+            let amount = encode_base10(amount, 8);
+            ret.insert(ticker, (amount, net_name.to_string(), net_addr, drk_addr));
         }
+
+        jsonrpc::response(json!(ret), id).into()
     }
 }

+ 12 - 3
bin/faucetd/src/main.rs

@@ -23,7 +23,7 @@ use darkfi::{
         Timestamp, Tx, ValidatorState, ValidatorStatePtr, MAINNET_GENESIS_HASH_BYTES,
         TESTNET_GENESIS_HASH_BYTES,
     },
-    crypto::{address::Address, keypair::PublicKey, types::DrkTokenId},
+    crypto::{address::Address, keypair::PublicKey, token_list::DrkTokenList, types::DrkTokenId},
     net,
     net::P2pPtr,
     node::Client,
@@ -358,6 +358,13 @@ async fn realmain(args: Args, ex: Arc<Executor<'_>>) -> Result<()> {
     )
     .await?;
 
+    let tokenlist = DrkTokenList::new(&[
+        ("drk", include_bytes!("../../../contrib/token/darkfi_token_list.min.json")),
+        ("btc", include_bytes!("../../../contrib/token/bitcoin_token_list.min.json")),
+        ("eth", include_bytes!("../../../contrib/token/erc20_token_list.min.json")),
+        ("sol", include_bytes!("../../../contrib/token/solana_token_list.min.json")),
+    ])?;
+
     // P2P network. The faucet doesn't participate in consensus, so we only
     // build the sync protocol.
     let network_settings = net::Settings {
@@ -374,10 +381,12 @@ async fn realmain(args: Args, ex: Arc<Executor<'_>>) -> Result<()> {
 
     info!("Registering block sync P2P protocols...");
     let _state = state.clone();
+    let _tokenlist = tokenlist.clone();
     registry
         .register(net::SESSION_ALL, move |channel, p2p| {
             let state = _state.clone();
-            async move { ProtocolSync::init(channel, state, p2p, false).await.unwrap() }
+            let tokenlist = _tokenlist.clone();
+            async move { ProtocolSync::init(channel, state, tokenlist, p2p, false).await.unwrap() }
         })
         .await;
 
@@ -415,7 +424,7 @@ async fn realmain(args: Args, ex: Arc<Executor<'_>>) -> Result<()> {
     })
     .detach();
 
-    match block_sync_task(sync_p2p.clone(), state.clone()).await {
+    match block_sync_task(sync_p2p.clone(), state.clone(), &tokenlist).await {
         Ok(()) => *faucetd.synced.lock().await = true,
         Err(e) => error!("Failed syncing blockchain: {}", e),
     }

+ 2 - 2
example/tx.rs

@@ -9,7 +9,7 @@ use darkfi::{
         note::{EncryptedNote, Note},
         nullifier::Nullifier,
         proof::{ProvingKey, VerifyingKey},
-        token_id::generate_id2,
+        token_id::generate_id,
         OwnCoin, OwnCoins,
     },
     node::state::{state_transition, ProgramState, StateUpdate},
@@ -143,7 +143,7 @@ fn main() -> Result<()> {
     };
 
     let token_id =
-        generate_id2("So11111111111111111111111111111111111111112", &NetworkName::Solana)?;
+        generate_id(&NetworkName::Solana, "So11111111111111111111111111111111111111112")?;
 
     let builder = TransactionBuilder {
         clear_inputs: vec![TransactionBuilderClearInputInfo {

+ 3 - 1
script/sql/coins.sql

@@ -4,7 +4,9 @@ CREATE TABLE IF NOT EXISTS coins(
 	coin_blind BLOB NOT NULL,
 	valcom_blind BLOB NOT NULL,
 	value BLOB NOT NULL,
-	token_id BLOB NOT NULL,
+	network BLOB NOT NULL,
+	drk_address BLOB NOT NULL,
+	net_address BLOB NOT NULL,
 	secret BLOB NOT NULL,
 	is_spent BOOLEAN NOT NULL,
 	nullifier BLOB NOT NULL,

+ 11 - 1
src/consensus/proto/protocol_sync.rs

@@ -8,6 +8,7 @@ use crate::{
         block::{BlockInfo, BlockOrder, BlockResponse},
         ValidatorState, ValidatorStatePtr,
     },
+    crypto::token_list::DrkTokenList,
     net::{
         ChannelPtr, MessageSubscription, P2pPtr, ProtocolBase, ProtocolBasePtr,
         ProtocolJobsManager, ProtocolJobsManagerPtr,
@@ -25,6 +26,7 @@ pub struct ProtocolSync {
     block_sub: MessageSubscription<BlockInfo>,
     jobsman: ProtocolJobsManagerPtr,
     state: ValidatorStatePtr,
+    tokenlist: DrkTokenList,
     p2p: P2pPtr,
     consensus_mode: bool,
 }
@@ -33,6 +35,7 @@ impl ProtocolSync {
     pub async fn init(
         channel: ChannelPtr,
         state: ValidatorStatePtr,
+        tokenlist: DrkTokenList,
         p2p: P2pPtr,
         consensus_mode: bool,
     ) -> Result<ProtocolBasePtr> {
@@ -49,6 +52,7 @@ impl ProtocolSync {
             block_sub,
             jobsman: ProtocolJobsManager::new("SyncProtocol", channel),
             state,
+            tokenlist,
             p2p,
             consensus_mode,
         }))
@@ -133,7 +137,13 @@ impl ProtocolSync {
                     debug!("ProtocolSync::handle_receive_block(): All state transitions passed");
 
                     debug!("ProtocolSync::handle_receive_block(): Updating canon state machine");
-                    match self.state.write().await.update_canon_state(state_updates, None).await {
+                    match self
+                        .state
+                        .write()
+                        .await
+                        .update_canon_state(state_updates, &self.tokenlist, None)
+                        .await
+                    {
                         Ok(()) => {}
                         Err(e) => {
                             error!("handle_receive_block(): Canon statemachine update fail: {}", e);

+ 9 - 1
src/consensus/state.rs

@@ -21,6 +21,7 @@ use crate::{
         address::Address,
         keypair::{PublicKey, SecretKey},
         schnorr::{SchnorrPublic, SchnorrSecret},
+        token_list::DrkTokenList,
     },
     net,
     node::{
@@ -855,6 +856,7 @@ impl ValidatorState {
     pub async fn update_canon_state(
         &self,
         updates: Vec<StateUpdate>,
+        tokenlist: &DrkTokenList,
         notify: Option<async_channel::Sender<(PublicKey, u64)>>,
     ) -> Result<()> {
         let secret_keys: Vec<SecretKey> =
@@ -864,7 +866,13 @@ impl ValidatorState {
         let mut state = self.state_machine.lock().await;
         for update in updates {
             state
-                .apply(update, secret_keys.clone(), notify.clone(), self.client.wallet.clone())
+                .apply(
+                    update,
+                    secret_keys.clone(),
+                    notify.clone(),
+                    self.client.wallet.clone(),
+                    tokenlist,
+                )
                 .await?;
         }
         drop(state);

+ 7 - 2
src/consensus/task/block_sync.rs

@@ -3,6 +3,7 @@ use crate::{
         block::{BlockOrder, BlockResponse},
         ValidatorState, ValidatorStatePtr,
     },
+    crypto::token_list::DrkTokenList,
     net,
     node::MemoryState,
     Result,
@@ -10,7 +11,11 @@ use crate::{
 use log::{debug, info, warn};
 
 /// async task used for block syncing.
-pub async fn block_sync_task(p2p: net::P2pPtr, state: ValidatorStatePtr) -> Result<()> {
+pub async fn block_sync_task(
+    p2p: net::P2pPtr,
+    state: ValidatorStatePtr,
+    tokenlist: &DrkTokenList,
+) -> Result<()> {
     info!("Starting blockchain sync...");
 
     // we retrieve p2p network connected channels, so we can use it to
@@ -58,7 +63,7 @@ pub async fn block_sync_task(p2p: net::P2pPtr, state: ValidatorStatePtr) -> Resu
             debug!("block_sync_task(): All state transitions passed");
 
             debug!("block_sync_task(): Updating canon state");
-            state.write().await.update_canon_state(canon_updates, None).await?;
+            state.write().await.update_canon_state(canon_updates, tokenlist, None).await?;
 
             debug!("block_sync_task(): Appending blocks to ledger");
             state.write().await.blockchain.add(&resp.blocks)?;

+ 5 - 52
src/crypto/note.rs

@@ -1,5 +1,3 @@
-use std::io;
-
 use crypto_api_chachapoly::ChachaPolyIetf;
 use rand::rngs::OsRng;
 
@@ -9,19 +7,16 @@ use crate::{
         keypair::{PublicKey, SecretKey},
         types::*,
     },
-    util::serial::{Decodable, Encodable, ReadExt, WriteExt},
+    util::serial::{Decodable, Encodable, SerialDecodable, SerialEncodable},
     Error, Result,
 };
 
-pub const NOTE_PLAINTEXT_SIZE: usize = 32 +    // serial
-    8 +     // value
-    32 +    // token_id
-    32 +    // coin_blind
-    32; // value_blind
+/// Plaintext size is serial + value + token_id + coin_blind + value_blind
+pub const NOTE_PLAINTEXT_SIZE: usize = 32 + 8 + 32 + 32 + 32;
 pub const AEAD_TAG_SIZE: usize = 16;
 pub const ENC_CIPHERTEXT_SIZE: usize = NOTE_PLAINTEXT_SIZE + AEAD_TAG_SIZE;
 
-#[derive(Copy, Clone, Debug, PartialEq)]
+#[derive(Copy, Clone, Debug, PartialEq, SerialEncodable, SerialDecodable)]
 pub struct Note {
     pub serial: DrkSerial,
     pub value: u64,
@@ -30,30 +25,6 @@ pub struct Note {
     pub value_blind: DrkValueBlind,
 }
 
-impl Encodable for Note {
-    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
-        let mut len = 0;
-        len += self.serial.encode(&mut s)?;
-        len += self.value.encode(&mut s)?;
-        len += self.token_id.encode(&mut s)?;
-        len += self.coin_blind.encode(&mut s)?;
-        len += self.value_blind.encode(&mut s)?;
-        Ok(len)
-    }
-}
-
-impl Decodable for Note {
-    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
-        Ok(Self {
-            serial: Decodable::decode(&mut d)?,
-            value: Decodable::decode(&mut d)?,
-            token_id: Decodable::decode(&mut d)?,
-            coin_blind: Decodable::decode(&mut d)?,
-            value_blind: Decodable::decode(d)?,
-        })
-    }
-}
-
 impl Note {
     pub fn encrypt(&self, public: &PublicKey) -> Result<EncryptedNote> {
         let ephem_secret = SecretKey::random(&mut OsRng);
@@ -76,30 +47,12 @@ impl Note {
     }
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, SerialEncodable, SerialDecodable)]
 pub struct EncryptedNote {
     ciphertext: [u8; ENC_CIPHERTEXT_SIZE],
     ephem_public: PublicKey,
 }
 
-impl Encodable for EncryptedNote {
-    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
-        let mut len = 0;
-        s.write_slice(&self.ciphertext)?;
-        len += ENC_CIPHERTEXT_SIZE;
-        len += self.ephem_public.encode(&mut s)?;
-        Ok(len)
-    }
-}
-
-impl Decodable for EncryptedNote {
-    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
-        let mut ciphertext = [0u8; ENC_CIPHERTEXT_SIZE];
-        d.read_slice(&mut ciphertext[..])?;
-        Ok(Self { ciphertext, ephem_public: Decodable::decode(d)? })
-    }
-}
-
 impl EncryptedNote {
     pub fn decrypt(&self, secret: &SecretKey) -> Result<Note> {
         let shared_secret = sapling_ka_agree(secret, &self.ephem_public);

+ 19 - 66
src/crypto/token_id.rs

@@ -1,74 +1,27 @@
-use sha2::{Digest, Sha256};
+use group::ff::PrimeField;
 
 use super::types::DrkTokenId;
-use crate::{
-    util::{
-        serial::{deserialize, serialize},
-        NetworkName,
-    },
-    Result,
-};
-
-/// Hash the external token ID and NetworkName param.
-/// If it fails, change the last 4 bytes and hash it again.
-/// Keep repeating until it works.
-pub fn generate_id(tkn_str: &str, network: &NetworkName) -> Result<DrkTokenId> {
-    let mut id_string = network.to_string();
-    id_string.push_str(tkn_str);
-
-    let mut data: Vec<u8> = serialize(&id_string);
-
-    let token_id = match deserialize::<DrkTokenId>(&data) {
-        Ok(v) => v,
-        Err(_) => {
-            let mut counter = 0;
-            loop {
-                data.truncate(28);
-                let serialized_counter = serialize(&counter);
-                data.extend(serialized_counter.iter());
-                let mut hasher = Sha256::new();
-                hasher.update(&data);
-                let hash = hasher.finalize();
-                let token_id = deserialize::<DrkTokenId>(&hash);
-                if token_id.is_err() {
-                    counter += 1;
-                    continue
-                }
-
-                return Ok(token_id.unwrap())
-            }
+use crate::{util::NetworkName, Result};
+
+pub fn generate_id(network: &NetworkName, token_str: &str) -> Result<DrkTokenId> {
+    let mut net_bytes: Vec<u8> = network.to_string().as_bytes().to_vec();
+    // TODO: Check for fixed length token_str
+    let mut token_bytes = match network {
+        NetworkName::DarkFi => {
+            let bytes = bs58::decode(token_str).into_vec()?;
+            return Ok(DrkTokenId::from_repr(bytes.try_into().unwrap()).unwrap())
         }
+        NetworkName::Bitcoin => bs58::decode(token_str).into_vec()?,
+        NetworkName::Ethereum => hex::decode(token_str.strip_prefix("0x").unwrap())?,
+        NetworkName::Solana => bs58::decode(token_str).into_vec()?,
     };
 
-    Ok(token_id)
-}
-
-/// YOLO
-pub fn generate_id2(tkn_str: &str, network: &NetworkName) -> Result<DrkTokenId> {
-    let mut num = 0_u64;
-
-    match network {
-        NetworkName::Solana => {
-            for i in ['s', 'o', 'l'] {
-                num += i as u64;
-            }
-        }
-        NetworkName::Bitcoin => {
-            for i in ['b', 't', 'c'] {
-                num += i as u64;
-            }
-        }
-        NetworkName::Ethereum => {
-            for i in ['e', 't', 'h'] {
-                num += i as u64;
-            }
-        }
-        NetworkName::Empty => unimplemented!(),
-    }
+    net_bytes.append(&mut token_bytes);
 
-    for i in tkn_str.chars() {
-        num += i as u64;
-    }
+    // Since our circuit is built to take a 2^64-1 range, we take the first 64
+    // bits of the hash and make an unsigned integer from it, which we can then
+    // cast into pallas::Base.
+    let data: [u8; 8] = blake3::hash(&net_bytes).as_bytes()[0..8].try_into()?;
 
-    Ok(DrkTokenId::from(num))
+    Ok(DrkTokenId::from(u64::from_le_bytes(data)))
 }

+ 51 - 246
src/crypto/token_list.rs

@@ -1,272 +1,77 @@
+use std::str::FromStr;
+
 use fxhash::FxHashMap;
+use group::ff::PrimeField;
 use serde_json::Value;
 
-use crate::{
-    crypto::{token_id::generate_id2, types::DrkTokenId},
-    util::NetworkName,
-    Error, Result,
-};
-
-pub const ETH_NATIVE_TOKEN_ID: &str = "0x0000000000000000000000000000000000000000";
+use super::{token_id::generate_id, types::DrkTokenId};
+use crate::{util::NetworkName, Result};
 
-#[derive(Debug, Clone)]
-pub struct TokenList {
-    tokens: Vec<Value>,
+#[derive(Clone, Debug)]
+pub struct TokenInfo {
+    pub net_address: String,
+    pub drk_address: DrkTokenId,
+    pub name: String,
+    pub decimals: u64,
 }
 
+#[derive(Clone)]
+pub struct TokenList(pub FxHashMap<String, TokenInfo>);
+
 impl TokenList {
-    pub fn new(data: &[u8]) -> Result<Self> {
+    /// Create a new `TokenList` given a standard JSON object (as bytes)
+    pub fn new(network_name: &str, data: &[u8]) -> Result<Self> {
         let tokenlist: Value = serde_json::from_slice(data)?;
-        let tokens = tokenlist["tokens"].as_array().ok_or(Error::TokenParseError)?.clone();
-        Ok(Self { tokens })
-    }
 
-    pub fn get_symbols(&self) -> Result<Vec<String>> {
-        let mut symbols: Vec<String> = Vec::new();
-        for item in self.tokens.iter() {
-            let symbol = item["symbol"].as_str().unwrap();
-            symbols.push(symbol.to_string());
-        }
-        Ok(symbols)
-    }
+        let mut map = FxHashMap::default();
+        for i in tokenlist["tokens"].as_array().unwrap() {
+            let net_address = i["address"].as_str().unwrap().to_string();
+            let decimals = i["decimals"].as_u64().unwrap();
+            let name = i["name"].as_str().unwrap().to_string();
+            let drk_address = generate_id(&NetworkName::from_str(network_name)?, &net_address)?;
 
-    pub fn search_id(&self, symbol: &str) -> Result<Option<String>> {
-        for item in self.tokens.iter() {
-            if item["symbol"] == symbol.to_uppercase() {
-                let address = item["address"].clone();
-                let address = address.as_str().ok_or(Error::TokenParseError)?;
-                return Ok(Some(address.to_string()))
-            }
+            let info = TokenInfo { net_address, drk_address, decimals, name };
+            let ticker = i["symbol"].as_str().unwrap().to_uppercase().to_string();
+            map.insert(ticker, info);
         }
-        Ok(None)
+
+        Ok(Self(map))
     }
 
-    pub fn search_decimal(&self, symbol: &str) -> Result<Option<usize>> {
-        for item in self.tokens.iter() {
-            if item["symbol"] == symbol.to_uppercase() {
-                let decimals = item["decimals"].clone();
-                let decimals = decimals.as_u64().ok_or(Error::TokenParseError)?;
-                let decimals = decimals as usize;
-                return Ok(Some(decimals))
-            }
+    /// Tries to find the address and name of a given ticker in
+    /// the hashmap.
+    pub fn get(&self, ticker: String) -> Option<TokenInfo> {
+        if let Some(info) = self.0.get(&ticker) {
+            return Some(info.clone())
         }
-        Ok(None)
+
+        None
     }
 }
 
-#[derive(Debug, Clone)]
+#[derive(Clone)]
 pub struct DrkTokenList {
-    pub tokens: FxHashMap<NetworkName, FxHashMap<String, DrkTokenId>>,
+    pub by_net: FxHashMap<NetworkName, TokenList>,
+    pub by_addr: FxHashMap<String, (NetworkName, TokenInfo)>,
 }
 
 impl DrkTokenList {
-    pub fn new(sol_list: &TokenList, eth_list: &TokenList, btc_list: &TokenList) -> Result<Self> {
-        let sol_symbols = sol_list.get_symbols()?;
-        let eth_symbols = eth_list.get_symbols()?;
-        let btc_symbols = btc_list.get_symbols()?;
-
-        let sol_tokens: FxHashMap<String, DrkTokenId> = sol_symbols
-            .iter()
-            .filter_map(|symbol| {
-                Self::generate_hash_pair(sol_list, &NetworkName::Solana, symbol).ok()
-            })
-            .collect();
-
-        let eth_tokens: FxHashMap<String, DrkTokenId> = eth_symbols
-            .iter()
-            .filter_map(|symbol| {
-                Self::generate_hash_pair(eth_list, &NetworkName::Ethereum, symbol).ok()
-            })
-            .collect();
-
-        let btc_tokens: FxHashMap<String, DrkTokenId> = btc_symbols
-            .iter()
-            .filter_map(|symbol| {
-                Self::generate_hash_pair(btc_list, &NetworkName::Bitcoin, symbol).ok()
-            })
-            .collect();
-
-        let mut tokens: FxHashMap<NetworkName, FxHashMap<String, DrkTokenId>> =
-            FxHashMap::default();
-
-        tokens.insert(NetworkName::Solana, sol_tokens);
-        tokens.insert(NetworkName::Ethereum, eth_tokens);
-        tokens.insert(NetworkName::Bitcoin, btc_tokens);
-
-        Ok(Self { tokens })
-    }
-
-    fn generate_hash_pair(
-        token_list: &TokenList,
-        network_name: &NetworkName,
-        symbol: &str,
-    ) -> Result<(String, DrkTokenId)> {
-        if let Some(token_id) = &token_list.search_id(symbol)? {
-            return Ok((symbol.to_string(), generate_id2(token_id, network_name)?))
-        };
-
-        Err(Error::UnsupportedToken)
-    }
-
-    pub fn symbol_from_id(&self, id: &DrkTokenId) -> Result<Option<(NetworkName, String)>> {
-        for (network, tokens) in self.tokens.iter() {
-            for (key, val) in tokens.iter() {
-                if val == id {
-                    return Ok(Some((network.clone(), key.clone())))
-                }
-            }
-        }
-
-        Ok(None)
-    }
-}
-
-pub fn assign_id(
-    network: &NetworkName,
-    token: &str,
-    sol_tokenlist: &TokenList,
-    eth_tokenlist: &TokenList,
-    btc_tokenlist: &TokenList,
-) -> Result<String> {
-    match network {
-        NetworkName::Solana => {
-            // (== 44) can represent a Solana base58 token mint address
-            if token.len() == 44 {
-                Ok(token.to_string())
-            } else {
-                let tok_lower = token.to_lowercase();
-                symbol_to_id(&tok_lower, sol_tokenlist)
-            }
-        }
-        NetworkName::Bitcoin => {
-            if token.len() == 34 {
-                Ok(token.to_string())
-            } else {
-                let tok_lower = token.to_lowercase();
-                symbol_to_id(&tok_lower, btc_tokenlist)
-            }
-        }
-        NetworkName::Ethereum => {
-            // (== 42) can represent a erc20 token mint address
-            if token.len() == 42 {
-                Ok(token.to_string())
-            } else if token == "eth" {
-                Ok(ETH_NATIVE_TOKEN_ID.to_string())
-            } else {
-                let tok_lower = token.to_lowercase();
-                symbol_to_id(&tok_lower, eth_tokenlist)
+    pub fn new(data: &[(&str, &[u8])]) -> Result<Self> {
+        let mut by_net = FxHashMap::default();
+        let mut by_addr = FxHashMap::default();
+
+        for (name, json) in data {
+            let net_name = NetworkName::from_str(name)?;
+            let tokenlist = TokenList::new(name, json)?;
+            for (_, token) in tokenlist.0.iter() {
+                by_addr.insert(
+                    bs58::encode(token.drk_address.to_repr()).into_string(),
+                    (net_name.clone(), token.clone()),
+                );
             }
+            by_net.insert(net_name, tokenlist);
         }
-        _ => Err(Error::UnsupportedCoinNetwork),
-    }
-}
-
-pub fn symbol_to_id(token: &str, tokenlist: &TokenList) -> Result<String> {
-    let vec: Vec<char> = token.chars().collect();
-    let mut counter = 0;
-    for c in vec {
-        if c.is_alphabetic() {
-            counter += 1;
-        }
-    }
-    if counter == token.len() {
-        if let Some(id) = tokenlist.search_id(token)? {
-            Ok(id)
-        } else {
-            Err(Error::TokenParseError)
-        }
-    } else {
-        Ok(token.to_string())
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    fn _get_sol_tokens() -> Result<TokenList> {
-        let file_contents = include_bytes!("../../tests/data/solanatokenlisttest.json");
-        let sol_tokenlist: Value = serde_json::from_slice(file_contents)?;
-
-        let tokens = sol_tokenlist["tokens"].as_array().ok_or(Error::TokenParseError)?.clone();
-
-        let sol_tokenlist = TokenList { tokens };
-        Ok(sol_tokenlist)
-    }
-
-    fn _get_eth_tokens() -> Result<TokenList> {
-        let file_contents = include_bytes!("../../tests/data/erc20tokenlisttest.json");
-        let eth_tokenlist: Value = serde_json::from_slice(file_contents)?;
-
-        let tokens = eth_tokenlist["tokens"].as_array().ok_or(Error::TokenParseError)?.clone();
-
-        let eth_tokenlist = TokenList { tokens };
-        Ok(eth_tokenlist)
-    }
-
-    fn _get_btc_tokens() -> Result<TokenList> {
-        let file_contents = include_bytes!("../../contrib/token/bitcoin_token_list.json");
-        let btc_tokenlist: Value = serde_json::from_slice(file_contents)?;
-
-        let tokens = btc_tokenlist["tokens"].as_array().ok_or(Error::TokenParseError)?.clone();
-
-        let btc_tokenlist = TokenList { tokens };
-        Ok(btc_tokenlist)
-    }
-
-    #[test]
-    pub fn test_get_symbols() -> Result<()> {
-        let tokens = _get_sol_tokens()?;
-        let symbols = tokens.get_symbols()?;
-        assert_eq!(symbols.len(), 5);
-        assert_eq!("MILLI", symbols[0]);
-        assert_eq!("ZI", symbols[1]);
-        assert_eq!("SOLA", symbols[2]);
-        assert_eq!("SOL", symbols[3]);
-        assert_eq!("USDC", symbols[4]);
-        Ok(())
-    }
-
-    #[test]
-    pub fn test_get_id_from_symbols() -> Result<()> {
-        let tokens = _get_sol_tokens()?;
-        let symbol = &tokens.get_symbols()?[3];
-        let id = tokens.search_id(symbol)?;
-        assert!(id.is_some());
-        assert_eq!(id.unwrap(), "So11111111111111111111111111111111111111112");
-        Ok(())
-    }
-
-    #[test]
-    pub fn test_hashmap() -> Result<()> {
-        let sol_tokens = _get_sol_tokens()?;
-        let sol_tokens2 = _get_sol_tokens()?;
-        let eth_tokens = _get_eth_tokens()?;
-        let eth_tokens2 = _get_eth_tokens()?;
-        let btc_tokens = _get_btc_tokens()?;
-        let btc_tokens2 = _get_btc_tokens()?;
-
-        let drk_token = DrkTokenList::new(&sol_tokens, &eth_tokens, &btc_tokens)?;
-
-        assert_eq!(drk_token.tokens[&NetworkName::Solana].len(), 5);
-        assert_eq!(drk_token.tokens[&NetworkName::Ethereum].len(), 3);
-        assert_eq!(drk_token.tokens[&NetworkName::Bitcoin].len(), 1);
-
-        assert_eq!(
-            drk_token.tokens[&NetworkName::Solana]["SOL"],
-            generate_id2(&sol_tokens2.search_id("SOL")?.unwrap(), &NetworkName::Solana)?
-        );
-
-        assert_eq!(
-            drk_token.tokens[&NetworkName::Bitcoin]["BTC"],
-            generate_id2(&btc_tokens2.search_id("BTC")?.unwrap(), &NetworkName::Bitcoin)?
-        );
-
-        assert_eq!(
-            drk_token.tokens[&NetworkName::Ethereum]["WBTC"],
-            generate_id2(&eth_tokens2.search_id("WBTC")?.unwrap(), &NetworkName::Ethereum)?
-        );
 
-        Ok(())
+        Ok(Self { by_net, by_addr })
     }
 }

+ 7 - 0
src/error.rs

@@ -46,6 +46,9 @@ pub enum Error {
     #[error("Could not parse token parameter")]
     TokenParseError,
 
+    #[error(transparent)]
+    TryFromSliceError(#[from] std::array::TryFromSliceError),
+
     // ===============
     // Encoding errors
     // ===============
@@ -80,6 +83,10 @@ pub enum Error {
     #[error(transparent)]
     Bs58DecodeError(#[from] bs58::decode::Error),
 
+    #[cfg(feature = "hex")]
+    #[error(transparent)]
+    HexDecodeError(#[from] hex::FromHexError),
+
     #[error("Bad operation type byte")]
     BadOperationType,
 

+ 3 - 1
src/node/state.rs

@@ -11,6 +11,7 @@ use crate::{
         note::{EncryptedNote, Note},
         nullifier::Nullifier,
         proof::VerifyingKey,
+        token_list::DrkTokenList,
         OwnCoin,
     },
     tx::Transaction,
@@ -137,6 +138,7 @@ impl State {
         secret_keys: Vec<SecretKey>,
         notify: Option<async_channel::Sender<(PublicKey, u64)>>,
         wallet: WalletPtr,
+        tokenlist: &DrkTokenList,
     ) -> Result<()> {
         debug!(target: "state_apply", "Extend nullifier set");
         self.nullifiers.insert(&update.nullifiers)?;
@@ -158,7 +160,7 @@ impl State {
                     let own_coin =
                         OwnCoin { coin, note, secret: *secret, nullifier, leaf_position };
 
-                    wallet.put_own_coin(own_coin).await?;
+                    wallet.put_own_coin(own_coin, tokenlist).await?;
 
                     if let Some(ch) = notify.clone() {
                         debug!(target: "state_apply", "Send a notification");

+ 5 - 4
src/util/net_name.rs

@@ -9,15 +9,18 @@ use crate::{
 
 #[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
 pub enum NetworkName {
+    DarkFi,
     Solana,
     Bitcoin,
     Ethereum,
-    Empty,
 }
 
 impl std::fmt::Display for NetworkName {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
+            Self::DarkFi => {
+                write!(f, "DarkFi")
+            }
             Self::Solana => {
                 write!(f, "Solana")
             }
@@ -27,9 +30,6 @@ impl std::fmt::Display for NetworkName {
             Self::Ethereum => {
                 write!(f, "Ethereum")
             }
-            Self::Empty => {
-                write!(f, "No Supported Network")
-            }
         }
     }
 }
@@ -39,6 +39,7 @@ impl FromStr for NetworkName {
 
     fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
         match s.to_lowercase().as_str() {
+            "drk" | "darkfi" => Ok(NetworkName::DarkFi),
             "sol" | "solana" => Ok(NetworkName::Solana),
             "btc" | "bitcoin" => Ok(NetworkName::Bitcoin),
             "eth" | "ethereum" => Ok(NetworkName::Ethereum),

+ 45 - 23
src/wallet/walletdb.rs

@@ -1,8 +1,9 @@
 use std::{fs::create_dir_all, path::Path, str::FromStr, time::Duration};
 
 use async_std::sync::Arc;
+use group::ff::PrimeField;
 use incrementalmerkletree::bridgetree::BridgeTree;
-use log::{debug, error, info, LevelFilter};
+use log::{debug, error, info, warn, LevelFilter};
 use rand::rngs::OsRng;
 use sqlx::{
     sqlite::{SqliteConnectOptions, SqliteJournalMode},
@@ -17,12 +18,14 @@ use crate::{
         merkle_node::MerkleNode,
         note::Note,
         nullifier::Nullifier,
+        token_list::DrkTokenList,
         types::DrkTokenId,
         OwnCoin, OwnCoins,
     },
     util::{
         expand_path,
         serial::{deserialize, serialize},
+        NetworkName,
     },
     Error::{WalletEmptyPassword, WalletTreeExists},
     Result,
@@ -267,7 +270,7 @@ impl WalletDb {
             // TODO: FIXME:
             let value_bytes: Vec<u8> = row.get("value");
             let value = u64::from_le_bytes(value_bytes.try_into().unwrap());
-            let token_id = deserialize(row.get("token_id"))?;
+            let token_id = deserialize(row.get("drk_address"))?;
             let note = Note { serial, value, token_id, coin_blind, value_blind };
 
             let secret = deserialize(row.get("secret"))?;
@@ -282,7 +285,7 @@ impl WalletDb {
         Ok(own_coins)
     }
 
-    pub async fn put_own_coin(&self, own_coin: OwnCoin) -> Result<()> {
+    pub async fn put_own_coin(&self, own_coin: OwnCoin, tokenlist: &DrkTokenList) -> Result<()> {
         debug!("Putting own coin into wallet database");
 
         let coin = serialize(&own_coin.coin.to_bytes());
@@ -290,27 +293,38 @@ impl WalletDb {
         let coin_blind = serialize(&own_coin.note.coin_blind);
         let value_blind = serialize(&own_coin.note.value_blind);
         let value = own_coin.note.value.to_le_bytes();
-        let token_id = serialize(&own_coin.note.token_id);
+        let drk_address = serialize(&own_coin.note.token_id);
         let secret = serialize(&own_coin.secret);
-        let is_spent: u32 = 0;
         let nullifier = serialize(&own_coin.nullifier);
         let leaf_position = serialize(&own_coin.leaf_position);
+        let is_spent: u32 = 0;
+
+        let token_id_enc = bs58::encode(&own_coin.note.token_id.to_repr()).into_string();
+        let (network, net_address) =
+            if let Some((network, token_info)) = tokenlist.by_addr.get(&token_id_enc) {
+                (network, token_info.net_address.clone())
+            } else {
+                warn!("Could not find network and token info in parsed token list");
+                (&NetworkName::DarkFi, "unknown".to_string())
+            };
 
         let mut conn = self.conn.acquire().await?;
         sqlx::query(
             "INSERT OR REPLACE INTO coins
-            (coin, serial, value, token_id, coin_blind,
-             valcom_blind, secret, is_spent, nullifier,
-             leaf_position)
+            (coin, serial, coin_blind, valcom_blind, value,
+             network, drk_address, net_address,
+             secret, is_spent, nullifier, leaf_position)
             VALUES
-             (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10);",
+             (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12);",
         )
         .bind(coin)
         .bind(serial)
-        .bind(value.to_vec())
-        .bind(token_id)
         .bind(coin_blind)
         .bind(value_blind)
+        .bind(value.to_vec())
+        .bind(serialize(network))
+        .bind(drk_address) // token_id
+        .bind(net_address)
         .bind(secret)
         .bind(is_spent)
         .bind(nullifier)
@@ -348,10 +362,11 @@ impl WalletDb {
         let is_spent = 0;
 
         let mut conn = self.conn.acquire().await?;
-        let rows = sqlx::query("SELECT value, token_id, nullifier FROM coins WHERE is_spent = ?1;")
-            .bind(is_spent)
-            .fetch_all(&mut conn)
-            .await?;
+        let rows =
+            sqlx::query("SELECT value, drk_address, nullifier FROM coins WHERE is_spent = ?1;")
+                .bind(is_spent)
+                .fetch_all(&mut conn)
+                .await?;
 
         debug!("Found {} rows", rows.len());
 
@@ -360,7 +375,7 @@ impl WalletDb {
             // TODO: FIXME:
             let value_bytes: Vec<u8> = row.get("value");
             let value = u64::from_le_bytes(value_bytes.try_into().unwrap());
-            let token_id = deserialize(row.get("token_id"))?;
+            let token_id = deserialize(row.get("drk_address"))?;
             let nullifier = deserialize(row.get("nullifier"))?;
             list.push(Balance { token_id, value, nullifier });
         }
@@ -373,14 +388,14 @@ impl WalletDb {
         let is_spent = 0;
 
         let mut conn = self.conn.acquire().await?;
-        let rows = sqlx::query("SELECT token_id FROM coins WHERE is_spent = ?1;")
+        let rows = sqlx::query("SELECT drk_address FROM coins WHERE is_spent = ?1;")
             .bind(is_spent)
             .fetch_all(&mut conn)
             .await?;
 
         let mut token_ids = vec![];
         for row in rows {
-            let token_id = deserialize(row.get("token_id"))?;
+            let token_id = deserialize(row.get("drk_address"))?;
             token_ids.push(token_id);
         }
 
@@ -395,7 +410,7 @@ impl WalletDb {
 
         let mut conn = self.conn.acquire().await?;
 
-        let id_check = sqlx::query("SELECT * FROM coins WHERE token_id = ?1 AND is_spent = ?2;")
+        let id_check = sqlx::query("SELECT * FROM coins WHERE drk_address = ?1 AND is_spent = ?2;")
             .bind(id)
             .bind(is_spent)
             .fetch_optional(&mut conn)
@@ -448,6 +463,13 @@ mod tests {
         let wallet = WalletDb::new("sqlite::memory:", WPASS).await?;
         let keypair = Keypair::random(&mut OsRng);
 
+        let tokenlist = DrkTokenList::new(&[
+            ("drk", include_bytes!("../../contrib/token/darkfi_token_list.min.json")),
+            ("btc", include_bytes!("../../contrib/token/bitcoin_token_list.min.json")),
+            ("eth", include_bytes!("../../contrib/token/erc20_token_list.min.json")),
+            ("sol", include_bytes!("../../contrib/token/solana_token_list.min.json")),
+        ])?;
+
         // init_db()
         wallet.init_db().await?;
 
@@ -465,19 +487,19 @@ mod tests {
         let c3 = dummy_coin(&keypair.secret, 11, &token_id);
 
         // put_own_coin()
-        wallet.put_own_coin(c0).await?;
+        wallet.put_own_coin(c0, &tokenlist).await?;
         tree1.append(&MerkleNode::from_coin(&c0.coin));
         tree1.witness();
 
-        wallet.put_own_coin(c1).await?;
+        wallet.put_own_coin(c1, &tokenlist).await?;
         tree1.append(&MerkleNode::from_coin(&c1.coin));
         tree1.witness();
 
-        wallet.put_own_coin(c2).await?;
+        wallet.put_own_coin(c2, &tokenlist).await?;
         tree1.append(&MerkleNode::from_coin(&c2.coin));
         tree1.witness();
 
-        wallet.put_own_coin(c3).await?;
+        wallet.put_own_coin(c3, &tokenlist).await?;
         tree1.append(&MerkleNode::from_coin(&c3.coin));
         tree1.witness();